<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package leaktest provides tools to detect leaked goroutines in tests.
// To use it, call "defer leaktest.AfterTest(t)()" at the beginning of each
// test that may use goroutines.
package leaktest

import (
	"runtime"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/util/timeutil"
)

// interestingGoroutines returns all goroutines we care about for the purpose
// of leak checking. It excludes testing or runtime ones.
func interestingGoroutines() (gs []string) {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	for _, g := range strings.Split(string(buf), "\n\n") {
		sl := strings.SplitN(g, "\n", 2)
		if len(sl) != 2 {
			continue
		}
		stack := strings.TrimSpace(sl[1])
		if strings.HasPrefix(stack, "testing.RunTests") {
			continue
		}

		if stack == "" ||
			strings.Contains(stack, "github.com/cockroachdb/cockroach/util/log.init") ||
			// TODO(peter): Until https://github.com/grpc/grpc-go/pull/751 or
			// something similar is done, opening a gRPC client connection does not
			// timeout properly. See also
			// https://github.com/cockroachdb/cockroach/issues/7524.
			strings.Contains(stack, "google.golang.org/grpc.NewConn") ||
			// Below are the stacks ignored by the upstream leaktest code.
			strings.Contains(stack, "testing.Main(") ||
			strings.Contains(stack, "testing.tRunner(") ||
			strings.Contains(stack, "runtime.goexit") ||
			strings.Contains(stack, "created by runtime.gc") ||
			strings.Contains(stack, "interestingGoroutines") ||
			strings.Contains(stack, "runtime.MHeap_Scavenger") ||
			strings.Contains(stack, "signal.signal_recv") ||
			strings.Contains(stack, "sigterm.handler") ||
			strings.Contains(stack, "runtime_mcall") ||
			strings.Contains(stack, "goroutine in C code") {
			continue
		}
		gs = append(gs, g)
	}
	sort.Strings(gs)
	return
}

// AfterTest snapshots the currently-running goroutines and returns a
// function to be run at the end of tests to see whether any
// goroutines leaked.
func AfterTest(t testing.TB) func() {
	orig := map[string]bool{}
	for _, g := range interestingGoroutines() {
		orig[g] = true
	}
	return func() {
		if t.Failed() {
			return
		}
		// Loop, waiting for goroutines to shut down.
		// Wait up to 5 seconds, but finish as quickly as possible.
		deadline := timeutil.Now().Add(5 * time.Second)
		for {
			var leaked []string
			for _, g := range interestingGoroutines() {
				if !orig[g] {
					leaked = append(leaked, g)
				}
			}
			if len(leaked) == 0 {
				return
			}
			if timeutil.Now().Before(deadline) {
				time.Sleep(50 * time.Millisecond)
				continue
			}
			for _, g := range leaked {
				t.Errorf("Leaked goroutine: %v", g)
			}
			return
		}
	}
}
<commit_msg>util/leaktest: ignore a go1.7 goroutine created by net.(*netFD).connect<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package leaktest provides tools to detect leaked goroutines in tests.
// To use it, call "defer leaktest.AfterTest(t)()" at the beginning of each
// test that may use goroutines.
package leaktest

import (
	"runtime"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/util/timeutil"
)

// interestingGoroutines returns all goroutines we care about for the purpose
// of leak checking. It excludes testing or runtime ones.
func interestingGoroutines() (gs []string) {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	for _, g := range strings.Split(string(buf), "\n\n") {
		sl := strings.SplitN(g, "\n", 2)
		if len(sl) != 2 {
			continue
		}
		stack := strings.TrimSpace(sl[1])
		if strings.HasPrefix(stack, "testing.RunTests") {
			continue
		}

		if stack == "" ||
			strings.Contains(stack, "github.com/cockroachdb/cockroach/util/log.init") ||
			// TODO(peter): Until https://github.com/grpc/grpc-go/pull/751 or
			// something similar is done, opening a gRPC client connection does not
			// timeout properly. See also
			// https://github.com/cockroachdb/cockroach/issues/7524.
			strings.Contains(stack, "google.golang.org/grpc.NewConn") ||
			// Go1.7 added a goroutine to network dialing that doesn't shut down
			// quickly.
			strings.Contains(stack, "created by net.(*netFD).connect") ||
			// Below are the stacks ignored by the upstream leaktest code.
			strings.Contains(stack, "testing.Main(") ||
			strings.Contains(stack, "testing.tRunner(") ||
			strings.Contains(stack, "runtime.goexit") ||
			strings.Contains(stack, "created by runtime.gc") ||
			strings.Contains(stack, "interestingGoroutines") ||
			strings.Contains(stack, "runtime.MHeap_Scavenger") ||
			strings.Contains(stack, "signal.signal_recv") ||
			strings.Contains(stack, "sigterm.handler") ||
			strings.Contains(stack, "runtime_mcall") ||
			strings.Contains(stack, "goroutine in C code") {
			continue
		}
		gs = append(gs, g)
	}
	sort.Strings(gs)
	return
}

// AfterTest snapshots the currently-running goroutines and returns a
// function to be run at the end of tests to see whether any
// goroutines leaked.
func AfterTest(t testing.TB) func() {
	orig := map[string]bool{}
	for _, g := range interestingGoroutines() {
		orig[g] = true
	}
	return func() {
		if t.Failed() {
			return
		}
		// Loop, waiting for goroutines to shut down.
		// Wait up to 5 seconds, but finish as quickly as possible.
		deadline := timeutil.Now().Add(5 * time.Second)
		for {
			var leaked []string
			for _, g := range interestingGoroutines() {
				if !orig[g] {
					leaked = append(leaked, g)
				}
			}
			if len(leaked) == 0 {
				return
			}
			if timeutil.Now().Before(deadline) {
				time.Sleep(50 * time.Millisecond)
				continue
			}
			for _, g := range leaked {
				t.Errorf("Leaked goroutine: %v", g)
			}
			return
		}
	}
}
<|endoftext|>


<commit_before>package integration

import (
	"flag"
	"fmt"
	"os"
	"runtime"
	"testing"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/docker/swarmkit/api"
	raftutils "github.com/docker/swarmkit/manager/state/raft/testutils"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/require"
)

var showTrace = flag.Bool("show-trace", false, "show stack trace after tests finish")

func printTrace() {
	var (
		buf       []byte
		stackSize int
	)
	bufferLen := 16384
	for stackSize == len(buf) {
		buf = make([]byte, bufferLen)
		stackSize = runtime.Stack(buf, true)
		bufferLen *= 2
	}
	buf = buf[:stackSize]
	logrus.Error("===========================STACK TRACE===========================")
	fmt.Println(string(buf))
	logrus.Error("===========================STACK TRACE END=======================")
}

func TestMain(m *testing.M) {
	flag.Parse()
	res := m.Run()
	if *showTrace {
		printTrace()
	}
	os.Exit(res)
}

// pollClusterReady calls control api until all conditions are true:
// * all nodes are ready
// * all managers has membership == accepted
// * all managers has reachability == reachable
// * one node is leader
// * number of workers and managers equals to expected
func pollClusterReady(t *testing.T, c *testCluster, numWorker, numManager int) {
	pollFunc := func() error {
		res, err := c.api.ListNodes(context.Background(), &api.ListNodesRequest{})
		if err != nil {
			return err
		}
		var mCount int
		var leaderFound bool
		for _, n := range res.Nodes {
			if n.Status.State != api.NodeStatus_READY {
				return fmt.Errorf("node %s with role %s isn't ready, status %s, message %s", n.ID, n.Spec.Role, n.Status.State, n.Status.Message)
			}
			if n.Spec.Membership != api.NodeMembershipAccepted {
				return fmt.Errorf("node %s with role %s isn't accepted to cluster, membership %s", n.ID, n.Spec.Role, n.Spec.Membership)
			}
			if n.Certificate.Role != n.Spec.Role {
				return fmt.Errorf("node %s had different roles in spec and certificate, %s and %s respectively", n.ID, n.Spec.Role, n.Certificate.Role)
			}
			if n.Certificate.Status.State != api.IssuanceStateIssued {
				return fmt.Errorf("node %s with role %s has no issued certificate, issuance state %s", n.ID, n.Spec.Role, n.Certificate.Status.State)
			}
			if n.Spec.Role == api.NodeRoleManager {
				if n.ManagerStatus == nil {
					return fmt.Errorf("manager node %s has no ManagerStatus field", n.ID)
				}
				if n.ManagerStatus.Reachability != api.RaftMemberStatus_REACHABLE {
					return fmt.Errorf("manager node %s has reachable status: %s", n.ID, n.ManagerStatus.Reachability)
				}
				mCount++
				if n.ManagerStatus.Leader {
					leaderFound = true
				}
			} else {
				if n.ManagerStatus != nil {
					return fmt.Errorf("worker node %s should not have manager status, returned %s", n.ID, n.ManagerStatus)
				}
			}
		}
		if !leaderFound {
			return fmt.Errorf("leader of cluster is not found")
		}
		wCount := len(res.Nodes) - mCount
		if mCount != numManager {
			return fmt.Errorf("unexpected number of managers: %d, expected %d", mCount, numManager)
		}
		if wCount != numWorker {
			return fmt.Errorf("unexpected number of workers: %d, expected %d", wCount, numWorker)
		}
		return nil
	}
	err := raftutils.PollFuncWithTimeout(nil, pollFunc, opsTimeout)
	require.NoError(t, err)
}

func pollServiceReady(t *testing.T, c *testCluster, sid string) {
	pollFunc := func() error {
		req := &api.ListTasksRequest{}
		res, err := c.api.ListTasks(context.Background(), req)
		require.NoError(t, err)
		if len(res.Tasks) == 0 {
			return fmt.Errorf("tasks list is empty")
		}
		for _, task := range res.Tasks {
			if task.Status.State != api.TaskStateRunning {
				return fmt.Errorf("task %s is not running, status %s", task.ID, task.Status.State)
			}
		}
		return nil
	}
	require.NoError(t, raftutils.PollFuncWithTimeout(nil, pollFunc, opsTimeout))
}

func newCluster(t *testing.T, numWorker, numManager int) *testCluster {
	cl := newTestCluster()
	for i := 0; i < numManager; i++ {
		require.NoError(t, cl.AddManager(), "manager number %d", i+1)
	}
	for i := 0; i < numWorker; i++ {
		require.NoError(t, cl.AddAgent(), "agent number %d", i+1)
	}

	pollClusterReady(t, cl, numWorker, numManager)
	return cl
}

func TestClusterCreate(t *testing.T) {
	numWorker, numManager := 0, 2
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()
}

func TestServiceCreate(t *testing.T) {
	numWorker, numManager := 3, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	sid, err := cl.CreateService("test_service", 60)
	require.NoError(t, err)
	pollServiceReady(t, cl, sid)
}

func TestNodeOps(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	// demote leader
	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	// remove node
	var worker *testNode
	for _, n := range cl.nodes {
		if !n.IsManager() && n.node.NodeID() != leader.node.NodeID() {
			worker = n
			break
		}
	}
	require.NoError(t, cl.RemoveNode(worker.node.NodeID(), false))
	// agents 1, managers 2
	numWorker--
	// long wait for heartbeat expiration
	pollClusterReady(t, cl, numWorker, numManager)

	// promote old leader back
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleManager))
	numWorker--
	numManager++
	// agents 0, managers 3
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemotePromote(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	var manager *testNode
	for _, n := range cl.nodes {
		if n.IsManager() && n.node.NodeID() != leader.node.NodeID() {
			manager = n
			break
		}
	}
	require.NoError(t, cl.SetNodeRole(manager.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	// promote same node
	require.NoError(t, cl.SetNodeRole(manager.node.NodeID(), api.NodeRoleManager))
	// agents 1, managers 3
	numWorker--
	numManager++
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestPromoteDemote(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	var worker *testNode
	for _, n := range cl.nodes {
		if !n.IsManager() {
			worker = n
			break
		}
	}
	require.NoError(t, cl.SetNodeRole(worker.node.NodeID(), api.NodeRoleManager))
	// agents 0, managers 4
	numWorker--
	numManager++
	pollClusterReady(t, cl, numWorker, numManager)

	// demote same node
	require.NoError(t, cl.SetNodeRole(worker.node.NodeID(), api.NodeRoleWorker))
	// agents 1, managers 3
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemotePromoteLeader(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	//promote former leader back
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleManager))
	// agents 1, managers 3
	numWorker--
	numManager++
	pollClusterReady(t, cl, numWorker, numManager)
}

// TODO: improve test to demote the leader in case of 2 remaining managers
func TestDemoteToSingleManager(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	leader, err = cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 3, managers 1
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemoteLeader(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemoteDownedManager(t *testing.T) {
	numWorker, numManager := 0, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)

	// add a new manager so we have 3, then find one (not the leader) to demote
	var demotee *testNode
	for _, n := range cl.nodes {
		if n.IsManager() && n.node.NodeID() != leader.node.NodeID() {
			demotee = n
			break
		}
	}

	nodeID := demotee.node.NodeID()

	resp, err := cl.api.GetNode(context.Background(), &api.GetNodeRequest{NodeID: nodeID})
	require.NoError(t, err)
	spec := resp.Node.Spec.Copy()
	spec.Role = api.NodeRoleWorker

	// stop the node, then demote it, and start it back up again so when it comes back up it has to realize
	// it's not running anymore
	require.NoError(t, demotee.Pause())

	// demote node, but don't use SetNodeRole, which waits until it successfully becomes a worker, since
	// the node is currently down
	require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error {
		_, err := cl.api.UpdateNode(context.Background(), &api.UpdateNodeRequest{
			NodeID:      nodeID,
			Spec:        spec,
			NodeVersion: &resp.Node.Meta.Version,
		})
		return err
	}, opsTimeout))

	// start it back up again
	require.NoError(t, cl.StartNode(nodeID))

	// wait to become worker
	require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error {
		if demotee.IsManager() {
			return fmt.Errorf("node is still not a worker")
		}
		return nil
	}, opsTimeout))

	// agents 1, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestRestartLeader(t *testing.T) {
	numWorker, numManager := 10, 5
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()
	leader, err := cl.Leader()
	require.NoError(t, err)

	origLeaderID := leader.node.NodeID()

	require.NoError(t, leader.Pause())

	require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error {
		resp, err := cl.api.GetNode(context.Background(), &api.GetNodeRequest{
			NodeID: origLeaderID,
		})
		if err != nil {
			return err
		}
		if resp.Node.Status.State != api.NodeStatus_DOWN {
			return errors.Errorf("node %s is still not down", origLeaderID)
		}
		return nil
	}, opsTimeout))

	require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error {
		resp, err := cl.api.ListNodes(context.Background(), &api.ListNodesRequest{})
		if err != nil {
			return err
		}
		for _, node := range resp.Nodes {
			if node.ID == origLeaderID {
				continue
			}
			require.False(t, node.Status.State == api.NodeStatus_DOWN, "nodes shouldn't go to down")
			if node.Status.State != api.NodeStatus_READY {
				return errors.Errorf("node %s is still not ready", node.ID)
			}
		}
		return nil
	}, opsTimeout))

	require.NoError(t, cl.StartNode(origLeaderID))

	pollClusterReady(t, cl, numWorker, numManager)
}
<commit_msg>integration: Speed up TestRestartLeader<commit_after>package integration

import (
	"flag"
	"fmt"
	"os"
	"runtime"
	"testing"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/docker/swarmkit/api"
	raftutils "github.com/docker/swarmkit/manager/state/raft/testutils"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/require"
)

var showTrace = flag.Bool("show-trace", false, "show stack trace after tests finish")

func printTrace() {
	var (
		buf       []byte
		stackSize int
	)
	bufferLen := 16384
	for stackSize == len(buf) {
		buf = make([]byte, bufferLen)
		stackSize = runtime.Stack(buf, true)
		bufferLen *= 2
	}
	buf = buf[:stackSize]
	logrus.Error("===========================STACK TRACE===========================")
	fmt.Println(string(buf))
	logrus.Error("===========================STACK TRACE END=======================")
}

func TestMain(m *testing.M) {
	flag.Parse()
	res := m.Run()
	if *showTrace {
		printTrace()
	}
	os.Exit(res)
}

// pollClusterReady calls control api until all conditions are true:
// * all nodes are ready
// * all managers has membership == accepted
// * all managers has reachability == reachable
// * one node is leader
// * number of workers and managers equals to expected
func pollClusterReady(t *testing.T, c *testCluster, numWorker, numManager int) {
	pollFunc := func() error {
		res, err := c.api.ListNodes(context.Background(), &api.ListNodesRequest{})
		if err != nil {
			return err
		}
		var mCount int
		var leaderFound bool
		for _, n := range res.Nodes {
			if n.Status.State != api.NodeStatus_READY {
				return fmt.Errorf("node %s with role %s isn't ready, status %s, message %s", n.ID, n.Spec.Role, n.Status.State, n.Status.Message)
			}
			if n.Spec.Membership != api.NodeMembershipAccepted {
				return fmt.Errorf("node %s with role %s isn't accepted to cluster, membership %s", n.ID, n.Spec.Role, n.Spec.Membership)
			}
			if n.Certificate.Role != n.Spec.Role {
				return fmt.Errorf("node %s had different roles in spec and certificate, %s and %s respectively", n.ID, n.Spec.Role, n.Certificate.Role)
			}
			if n.Certificate.Status.State != api.IssuanceStateIssued {
				return fmt.Errorf("node %s with role %s has no issued certificate, issuance state %s", n.ID, n.Spec.Role, n.Certificate.Status.State)
			}
			if n.Spec.Role == api.NodeRoleManager {
				if n.ManagerStatus == nil {
					return fmt.Errorf("manager node %s has no ManagerStatus field", n.ID)
				}
				if n.ManagerStatus.Reachability != api.RaftMemberStatus_REACHABLE {
					return fmt.Errorf("manager node %s has reachable status: %s", n.ID, n.ManagerStatus.Reachability)
				}
				mCount++
				if n.ManagerStatus.Leader {
					leaderFound = true
				}
			} else {
				if n.ManagerStatus != nil {
					return fmt.Errorf("worker node %s should not have manager status, returned %s", n.ID, n.ManagerStatus)
				}
			}
		}
		if !leaderFound {
			return fmt.Errorf("leader of cluster is not found")
		}
		wCount := len(res.Nodes) - mCount
		if mCount != numManager {
			return fmt.Errorf("unexpected number of managers: %d, expected %d", mCount, numManager)
		}
		if wCount != numWorker {
			return fmt.Errorf("unexpected number of workers: %d, expected %d", wCount, numWorker)
		}
		return nil
	}
	err := raftutils.PollFuncWithTimeout(nil, pollFunc, opsTimeout)
	require.NoError(t, err)
}

func pollServiceReady(t *testing.T, c *testCluster, sid string) {
	pollFunc := func() error {
		req := &api.ListTasksRequest{}
		res, err := c.api.ListTasks(context.Background(), req)
		require.NoError(t, err)
		if len(res.Tasks) == 0 {
			return fmt.Errorf("tasks list is empty")
		}
		for _, task := range res.Tasks {
			if task.Status.State != api.TaskStateRunning {
				return fmt.Errorf("task %s is not running, status %s", task.ID, task.Status.State)
			}
		}
		return nil
	}
	require.NoError(t, raftutils.PollFuncWithTimeout(nil, pollFunc, opsTimeout))
}

func newCluster(t *testing.T, numWorker, numManager int) *testCluster {
	cl := newTestCluster()
	for i := 0; i < numManager; i++ {
		require.NoError(t, cl.AddManager(), "manager number %d", i+1)
	}
	for i := 0; i < numWorker; i++ {
		require.NoError(t, cl.AddAgent(), "agent number %d", i+1)
	}

	pollClusterReady(t, cl, numWorker, numManager)
	return cl
}

func TestClusterCreate(t *testing.T) {
	numWorker, numManager := 0, 2
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()
}

func TestServiceCreate(t *testing.T) {
	numWorker, numManager := 3, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	sid, err := cl.CreateService("test_service", 60)
	require.NoError(t, err)
	pollServiceReady(t, cl, sid)
}

func TestNodeOps(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	// demote leader
	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	// remove node
	var worker *testNode
	for _, n := range cl.nodes {
		if !n.IsManager() && n.node.NodeID() != leader.node.NodeID() {
			worker = n
			break
		}
	}
	require.NoError(t, cl.RemoveNode(worker.node.NodeID(), false))
	// agents 1, managers 2
	numWorker--
	// long wait for heartbeat expiration
	pollClusterReady(t, cl, numWorker, numManager)

	// promote old leader back
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleManager))
	numWorker--
	numManager++
	// agents 0, managers 3
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemotePromote(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	var manager *testNode
	for _, n := range cl.nodes {
		if n.IsManager() && n.node.NodeID() != leader.node.NodeID() {
			manager = n
			break
		}
	}
	require.NoError(t, cl.SetNodeRole(manager.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	// promote same node
	require.NoError(t, cl.SetNodeRole(manager.node.NodeID(), api.NodeRoleManager))
	// agents 1, managers 3
	numWorker--
	numManager++
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestPromoteDemote(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	var worker *testNode
	for _, n := range cl.nodes {
		if !n.IsManager() {
			worker = n
			break
		}
	}
	require.NoError(t, cl.SetNodeRole(worker.node.NodeID(), api.NodeRoleManager))
	// agents 0, managers 4
	numWorker--
	numManager++
	pollClusterReady(t, cl, numWorker, numManager)

	// demote same node
	require.NoError(t, cl.SetNodeRole(worker.node.NodeID(), api.NodeRoleWorker))
	// agents 1, managers 3
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemotePromoteLeader(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	//promote former leader back
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleManager))
	// agents 1, managers 3
	numWorker--
	numManager++
	pollClusterReady(t, cl, numWorker, numManager)
}

// TODO: improve test to demote the leader in case of 2 remaining managers
func TestDemoteToSingleManager(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)

	leader, err = cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 3, managers 1
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemoteLeader(t *testing.T) {
	numWorker, numManager := 1, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)
	require.NoError(t, cl.SetNodeRole(leader.node.NodeID(), api.NodeRoleWorker))
	// agents 2, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestDemoteDownedManager(t *testing.T) {
	numWorker, numManager := 0, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()

	leader, err := cl.Leader()
	require.NoError(t, err)

	// add a new manager so we have 3, then find one (not the leader) to demote
	var demotee *testNode
	for _, n := range cl.nodes {
		if n.IsManager() && n.node.NodeID() != leader.node.NodeID() {
			demotee = n
			break
		}
	}

	nodeID := demotee.node.NodeID()

	resp, err := cl.api.GetNode(context.Background(), &api.GetNodeRequest{NodeID: nodeID})
	require.NoError(t, err)
	spec := resp.Node.Spec.Copy()
	spec.Role = api.NodeRoleWorker

	// stop the node, then demote it, and start it back up again so when it comes back up it has to realize
	// it's not running anymore
	require.NoError(t, demotee.Pause())

	// demote node, but don't use SetNodeRole, which waits until it successfully becomes a worker, since
	// the node is currently down
	require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error {
		_, err := cl.api.UpdateNode(context.Background(), &api.UpdateNodeRequest{
			NodeID:      nodeID,
			Spec:        spec,
			NodeVersion: &resp.Node.Meta.Version,
		})
		return err
	}, opsTimeout))

	// start it back up again
	require.NoError(t, cl.StartNode(nodeID))

	// wait to become worker
	require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error {
		if demotee.IsManager() {
			return fmt.Errorf("node is still not a worker")
		}
		return nil
	}, opsTimeout))

	// agents 1, managers 2
	numWorker++
	numManager--
	pollClusterReady(t, cl, numWorker, numManager)
}

func TestRestartLeader(t *testing.T) {
	numWorker, numManager := 5, 3
	cl := newCluster(t, numWorker, numManager)
	defer func() {
		require.NoError(t, cl.Stop())
	}()
	leader, err := cl.Leader()
	require.NoError(t, err)

	origLeaderID := leader.node.NodeID()

	require.NoError(t, leader.Pause())

	require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error {
		resp, err := cl.api.ListNodes(context.Background(), &api.ListNodesRequest{})
		if err != nil {
			return err
		}
		for _, node := range resp.Nodes {
			if node.ID == origLeaderID {
				continue
			}
			require.False(t, node.Status.State == api.NodeStatus_DOWN, "nodes shouldn't go to down")
			if node.Status.State != api.NodeStatus_READY {
				return errors.Errorf("node %s is still not ready", node.ID)
			}
		}
		return nil
	}, opsTimeout))

	require.NoError(t, cl.StartNode(origLeaderID))

	pollClusterReady(t, cl, numWorker, numManager)
}
<|endoftext|>


<commit_before>// +build linux

package common

import "os"

func NumProcs() (uint64, error) {
	f, err := os.Open(HostProc())
	if err != nil {
		return 0, err
	}

	list, err := f.Readdir(-1)
	defer f.Close()
	if err != nil {
		return 0, err
	}
	return uint64(len(list)), err
}
<commit_msg>Add DoSysctrl() to Linux's common utilities.<commit_after>// +build linux

package common

import (
	"os"
	"os/exec"
	"strings"
)

func DoSysctrl(mib string) ([]string, error) {
	err := os.Setenv("LC_ALL", "C")
	if err != nil {
		return []string{}, err
	}
	sysctl, err := exec.LookPath("/sbin/sysctl")
	if err != nil {
		return []string{}, err
	}
	out, err := exec.Command(sysctl, "-n", mib).Output()
	if err != nil {
		return []string{}, err
	}
	v := strings.Replace(string(out), "{ ", "", 1)
	v = strings.Replace(string(v), " }", "", 1)
	values := strings.Fields(string(v))

	return values, nil
}

func NumProcs() (uint64, error) {
	f, err := os.Open(HostProc())
	if err != nil {
		return 0, err
	}

	list, err := f.Readdir(-1)
	defer f.Close()
	if err != nil {
		return 0, err
	}
	return uint64(len(list)), err
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation.
//
// 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 version

import "fmt"

const MAJOR uint = 2
const MINOR uint = 25
const PATCH uint = 0

var COMMIT string = ""
var IDENTIFIER string = ""
var METADATA string = ""

func Version() string {
	var suffix string = ""
	if len(IDENTIFIER) > 0 {
		suffix = fmt.Sprintf("-%s", IDENTIFIER)
	}

	if len(COMMIT) > 0 || len(METADATA) > 0 {
		suffix = suffix + "+"
	}

	if len(COMMIT) > 0 {
		suffix = fmt.Sprintf("%s"+"commit.%s", suffix, COMMIT)

	}

	if len(METADATA) > 0 {
		if len(COMMIT) > 0 {
			suffix = suffix + "."
		}
		suffix = suffix + METADATA
	}

	return fmt.Sprintf("%d.%d.%d%s", MAJOR, MINOR, PATCH, suffix)
}
<commit_msg>v2.26.0<commit_after>// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation.
//
// 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 version

import "fmt"

const MAJOR uint = 2
const MINOR uint = 26
const PATCH uint = 0

var COMMIT string = ""
var IDENTIFIER string = ""
var METADATA string = ""

func Version() string {
	var suffix string = ""
	if len(IDENTIFIER) > 0 {
		suffix = fmt.Sprintf("-%s", IDENTIFIER)
	}

	if len(COMMIT) > 0 || len(METADATA) > 0 {
		suffix = suffix + "+"
	}

	if len(COMMIT) > 0 {
		suffix = fmt.Sprintf("%s"+"commit.%s", suffix, COMMIT)

	}

	if len(METADATA) > 0 {
		if len(COMMIT) > 0 {
			suffix = suffix + "."
		}
		suffix = suffix + METADATA
	}

	return fmt.Sprintf("%d.%d.%d%s", MAJOR, MINOR, PATCH, suffix)
}
<|endoftext|>


<commit_before>// Copyright 2018 DataArt. All rights reserved.
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.

package testutils

import (
	dh "github.com/devicehive/devicehive-go"
	"testing"
)

func LogDHErr(t *testing.T, err *dh.Error) {
	if err != nil {
		t.Errorf("%s: %v", err.Name(), err)
	}
}
<commit_msg>Delete unused testutils<commit_after><|endoftext|>


<commit_before>package arena

import (
	"encoding/json"
	"fmt"
	"io"
	"time"

	"github.com/asciimoo/solary/arena/board"
	"github.com/asciimoo/solary/arena/coord"
	"github.com/asciimoo/solary/player"
)

const (
	ROUND_TIMEOUT = 2
	MAX_ROUNDS    = 500
)

var battle_id uint = 0
var player_id uint = 0

type Arena struct {
	Id      uint
	Round   uint
	Players []*player.Player
	Board   *board.Board
}

func Create() *Arena {
	a := &Arena{
		battle_id,
		0,
		make([]*player.Player, 0),
		board.Create(),
	}
	battle_id += 1
	return a
}

func (a *Arena) Play() {
	ch := make(chan *player.Move)
	for _, p := range a.Players {
		defer p.Conn.Close()
		go p.Read(ch)
	}
	for {
		a.Round += 1
		a.broadcastStatus()
		if a.Round == MAX_ROUNDS || a.getActivePlayersNum() == 0 {
			fmt.Println("Game", a.Id, "finished")
			return
		}
		if a.Round%100 == 0 {
			a.Board.PopulateRandomLoot()
		}
		fmt.Println("Round", a.Round, "started")
		// collect moves
		moves := a.getMoves(ch)
		// activate laser beams and traps
		for _, move := range moves {
			if move.Item == "" {
				continue
			}
			c, ok := move.Player.Inventory[move.Item]
			if !ok || c <= 0 {
				continue
			}
			move.Player.Inventory[move.Item] -= 1
			switch move.Item {
			case "trap":
				a.Board.FieldByCoord(move.Player.Position).AddTrap()
			case "laser beam":
				laser_x := 0
				laser_y := 0
				switch move.Direction {
				case "up":
					laser_y -= 1
				case "down":
					laser_y += 1
				case "left":
					laser_x -= 1
				case "right":
					laser_x += 1
				}
				for i := 1; i <= 2; i++ {
					laser_coord := coord.Coord{
						uint(laser_x*i) + move.Player.Position.X,
						uint(laser_y*i) + move.Player.Position.Y,
					}
					for _, p := range a.Players {
						if p.Position.X == laser_coord.X && p.Position.Y == laser_coord.Y {
							p.Life -= 25
						}
					}
				}
			case "oil":
				if move.Player.Life > 80 {
					move.Player.Life = 100
				} else {
					move.Player.Life += 20
				}
			}
		}

		a.movePlayers(&moves)

		a.checkDeath()

		// helper coord->user map
		player_coords := make(map[coord.Coord][]*player.Player)
		for _, p := range a.Players {
			player_coords[p.Position] = append(player_coords[p.Position], p)
		}

		// activate destination traps && collect loot
		for c, players := range player_coords {
			field := a.Board.FieldByCoord(c)
			if len(players) == 1 {
				for _, l := range a.Board.FieldByCoord(c).Loot {
					players[0].Inventory[l] += 1
				}
				field.ClearLoot()
			}
			if field.Traps > 0 {
				for _, p := range players {
					p.Life -= 50 * a.Board.FieldByCoord(c).Traps
				}
				field.ClearTraps()
			}
		}

		a.checkDeath()

		// trigger inventory items
		for _, p := range a.Players {
			p.Score += p.Inventory["solar panel"]
		}
	}
}

func (a *Arena) movePlayers(moves *[]*player.Move) {
	for _, move := range *moves {
		a.movePlayer(move)
	}
}

func (a *Arena) checkDeath() {
	for _, p := range a.Players {
		if p.Life <= 0 {
			field := a.Board.FieldByCoord(p.Position)
			for loot, loot_count := range p.Inventory {
				p.Inventory[loot] = 0
				for i := 0; i < loot_count; i++ {
					field.Loot = append(field.Loot, loot)
				}
			}
			p.Life = 100
			p.Position.X = 0
			p.Position.Y = 0
		}
	}
}

func (a *Arena) movePlayer(m *player.Move) {
	new_y := m.Player.Position.Y
	new_x := m.Player.Position.X
	distance := uint(1)
	if m.Item == "pogo stick" {
		distance = 2
	}
	switch m.Direction {
	case "up":
		new_y -= distance
	case "down":
		new_y += distance
	case "left":
		new_x -= distance
	case "right":
		new_x += distance
	}
	if a.Board.IsValidLocation(int(new_x), int(new_y)) {
		m.Player.Position.X = new_x
		m.Player.Position.Y = new_y
	}
}

func (a *Arena) getMoves(ch chan *player.Move) []*player.Move {
	moves := []*player.Move{}
	timeout := make(chan bool)
	go func() {
		time.Sleep(ROUND_TIMEOUT * time.Second)
		timeout <- true
	}()
	recv_break := false
	for !recv_break && len(moves) < a.getActivePlayersNum() {
		select {
		case move := <-ch:
			if move.Error != nil {
				fmt.Println("user error:", move.Error)
				break
			}
			can_move := true
			for _, m := range moves {
				if m.Player == move.Player {
					can_move = false
					break
				}
			}
			if can_move {
				moves = append(moves, move)
			} else {
				fmt.Println("Error: already moved")
			}
		case <-timeout:
			fmt.Println("Timeout")
			recv_break = true
		}
	}
	return moves
}

func (a *Arena) getActivePlayersNum() int {
	active_players := 0
	for _, player := range a.Players {
		if !player.Disconnected {
			active_players += 1
		}
	}
	return active_players
}

func (a *Arena) broadcastStatus() {
	b, err := json.Marshal(a)
	if err != nil {
		fmt.Println(err)
	}
	for _, p := range a.Players {
		p.Write(b)
	}
}

func (a *Arena) AddPlayer(conn io.ReadWriteCloser) {
	p := player.Create(player_id, conn)
	msg, _ := json.Marshal(p)
	p.Write(msg)
	player_id += 1
	a.Players = append(a.Players, p)
}
<commit_msg>[fix] remove debug message<commit_after>package arena

import (
	"encoding/json"
	"fmt"
	"io"
	"time"

	"github.com/asciimoo/solary/arena/board"
	"github.com/asciimoo/solary/arena/coord"
	"github.com/asciimoo/solary/player"
)

const (
	ROUND_TIMEOUT = 2
	MAX_ROUNDS    = 500
)

var battle_id uint = 0
var player_id uint = 0

type Arena struct {
	Id      uint
	Round   uint
	Players []*player.Player
	Board   *board.Board
}

func Create() *Arena {
	a := &Arena{
		battle_id,
		0,
		make([]*player.Player, 0),
		board.Create(),
	}
	battle_id += 1
	return a
}

func (a *Arena) Play() {
	ch := make(chan *player.Move)
	for _, p := range a.Players {
		defer p.Conn.Close()
		go p.Read(ch)
	}
	for {
		a.Round += 1
		a.broadcastStatus()
		if a.Round == MAX_ROUNDS || a.getActivePlayersNum() == 0 {
			fmt.Println("Game", a.Id, "finished")
			return
		}
		if a.Round%100 == 0 {
			a.Board.PopulateRandomLoot()
		}
		// collect moves
		moves := a.getMoves(ch)
		// activate laser beams and traps
		for _, move := range moves {
			if move.Item == "" {
				continue
			}
			c, ok := move.Player.Inventory[move.Item]
			if !ok || c <= 0 {
				continue
			}
			move.Player.Inventory[move.Item] -= 1
			switch move.Item {
			case "trap":
				a.Board.FieldByCoord(move.Player.Position).AddTrap()
			case "laser beam":
				laser_x := 0
				laser_y := 0
				switch move.Direction {
				case "up":
					laser_y -= 1
				case "down":
					laser_y += 1
				case "left":
					laser_x -= 1
				case "right":
					laser_x += 1
				}
				for i := 1; i <= 2; i++ {
					laser_coord := coord.Coord{
						uint(laser_x*i) + move.Player.Position.X,
						uint(laser_y*i) + move.Player.Position.Y,
					}
					for _, p := range a.Players {
						if p.Position.X == laser_coord.X && p.Position.Y == laser_coord.Y {
							p.Life -= 25
						}
					}
				}
			case "oil":
				if move.Player.Life > 80 {
					move.Player.Life = 100
				} else {
					move.Player.Life += 20
				}
			}
		}

		a.movePlayers(&moves)

		a.checkDeath()

		// helper coord->user map
		player_coords := make(map[coord.Coord][]*player.Player)
		for _, p := range a.Players {
			player_coords[p.Position] = append(player_coords[p.Position], p)
		}

		// activate destination traps && collect loot
		for c, players := range player_coords {
			field := a.Board.FieldByCoord(c)
			if len(players) == 1 {
				for _, l := range a.Board.FieldByCoord(c).Loot {
					players[0].Inventory[l] += 1
				}
				field.ClearLoot()
			}
			if field.Traps > 0 {
				for _, p := range players {
					p.Life -= 50 * a.Board.FieldByCoord(c).Traps
				}
				field.ClearTraps()
			}
		}

		a.checkDeath()

		// trigger inventory items
		for _, p := range a.Players {
			p.Score += p.Inventory["solar panel"]
		}
	}
}

func (a *Arena) movePlayers(moves *[]*player.Move) {
	for _, move := range *moves {
		a.movePlayer(move)
	}
}

func (a *Arena) checkDeath() {
	for _, p := range a.Players {
		if p.Life <= 0 {
			field := a.Board.FieldByCoord(p.Position)
			for loot, loot_count := range p.Inventory {
				p.Inventory[loot] = 0
				for i := 0; i < loot_count; i++ {
					field.Loot = append(field.Loot, loot)
				}
			}
			p.Life = 100
			p.Position.X = 0
			p.Position.Y = 0
		}
	}
}

func (a *Arena) movePlayer(m *player.Move) {
	new_y := m.Player.Position.Y
	new_x := m.Player.Position.X
	distance := uint(1)
	if m.Item == "pogo stick" {
		distance = 2
	}
	switch m.Direction {
	case "up":
		new_y -= distance
	case "down":
		new_y += distance
	case "left":
		new_x -= distance
	case "right":
		new_x += distance
	}
	if a.Board.IsValidLocation(int(new_x), int(new_y)) {
		m.Player.Position.X = new_x
		m.Player.Position.Y = new_y
	}
}

func (a *Arena) getMoves(ch chan *player.Move) []*player.Move {
	moves := []*player.Move{}
	timeout := make(chan bool)
	go func() {
		time.Sleep(ROUND_TIMEOUT * time.Second)
		timeout <- true
	}()
	recv_break := false
	for !recv_break && len(moves) < a.getActivePlayersNum() {
		select {
		case move := <-ch:
			if move.Error != nil {
				fmt.Println("user error:", move.Error)
				break
			}
			can_move := true
			for _, m := range moves {
				if m.Player == move.Player {
					can_move = false
					break
				}
			}
			if can_move {
				moves = append(moves, move)
			} else {
				fmt.Println("Error: already moved")
			}
		case <-timeout:
			fmt.Println("Timeout")
			recv_break = true
		}
	}
	return moves
}

func (a *Arena) getActivePlayersNum() int {
	active_players := 0
	for _, player := range a.Players {
		if !player.Disconnected {
			active_players += 1
		}
	}
	return active_players
}

func (a *Arena) broadcastStatus() {
	b, err := json.Marshal(a)
	if err != nil {
		fmt.Println(err)
	}
	for _, p := range a.Players {
		p.Write(b)
	}
}

func (a *Arena) AddPlayer(conn io.ReadWriteCloser) {
	p := player.Create(player_id, conn)
	msg, _ := json.Marshal(p)
	p.Write(msg)
	player_id += 1
	a.Players = append(a.Players, p)
}
<|endoftext|>


<commit_before>package asana

import (
	"encoding/json"
	"fmt"
	"github.com/google/go-querystring/query"
	"net/http"
	"net/url"
	"reflect"
)

const (
	libraryVersion = "0.1"
	userAgent      = "go-asana/" + libraryVersion
	defaultBaseURL = "https://app.asana.com/api/1.0/"
)

var defaultOptFields = map[string]string{
	"tags":       "name,color,notes",
	"users":      "name,email,photo",
	"projects":   "name,color,archived",
	"workspaces": "name,is_organization",
	"tasks":      "name,assignee,assignee_status,completed,parent",
}

type (
	Client struct {
		client    *http.Client
		BaseURL   *url.URL
		UserAgent string
	}

	Workspace struct {
		ID           int64  `json:"id,omitempty"`
		Name         string `json:"name,omitempty"`
		Organization bool   `json:"is_organization,omitempty"`
	}

	User struct {
		ID         int64       `json:"id,omitempty"`
		Email      string      `json:"email,omitempty"`
		Name       string      `json:"name,omitempty"`
		Photo      string      `json:"photo,omitempty"`
		Workspaces []Workspace `json:"workspaces,omitempty"`
	}

	Project struct {
		ID       int64  `json:"id,omitempty"`
		Name     string `json:"name,omitempty"`
		Archived bool   `json:"archived,omitempty"`
		Color    string `json:"color,omitempty"`
		Notes    string `json:"notes,omitempty"`
	}

	Task struct {
		ID             int64     `json:"id,omitempty"`
		Assignee       *User     `json:"assignee,omitempty"`
		AssigneeStatus string    `json:"assignee_status,omitempty"`
		Completed      bool      `json:"completed,omitempty"`
		Name           string    `json:"name,omitempty"`
		Notes          string    `json:"notes,omitempty"`
		ParentTask     *Task     `json:"parent,omitempty"`
		Projects       []Project `json:"projects,omitempty"`
	}

	Tag struct {
		ID    int64  `json:"id,omitempty"`
		Name  string `json:"name,omitempty"`
		Color string `json:"color,omitempty"`
		Notes string `json:"notes,omitempty"`
	}

	Filter struct {
		Archived       bool   `url:"archived,omitempty"`
		Assignee       int64  `url:"assignee,omitempty"`
		Project        int64  `url:"project,omitempty"`
		Workspace      int64  `url:"workspace,omitempty"`
		CompletedSince string `url:"completed_since,omitempty"`
		ModifiedSince  string `url:"modified_since,omitempty"`
		OptFields      string `url:"opt_fields,omitempty"`
	}

	Response struct {
		Data   interface{} `json:"data,omitempty"`
		Errors []Error     `json:"errors,omitempty"`
	}

	Error struct {
		Phrase  string `json:"phrase,omitempty"`
		Message string `json:"message,omitempty"`
	}
)

func (e Error) Error() string {
	return fmt.Sprintf("%v - %v", e.Message, e.Phrase)
}

func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(defaultBaseURL)
	client := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent}
	return client
}

func (c *Client) ListWorkspaces() ([]Workspace, error) {
	workspaces := new([]Workspace)
	err := c.Request("workspaces", nil, workspaces)
	return *workspaces, err
}

func (c *Client) ListUsers(opt *Filter) ([]User, error) {
	users := new([]User)
	err := c.Request("users", opt, users)
	return *users, err
}

func (c *Client) ListProjects(opt *Filter) ([]Project, error) {
	projects := new([]Project)
	err := c.Request("projects", opt, projects)
	return *projects, err
}

func (c *Client) ListTasks(opt *Filter) ([]Task, error) {
	tasks := new([]Task)
	err := c.Request("tasks", opt, tasks)
	return *tasks, err
}

func (c *Client) ListTags(opt *Filter) ([]Tag, error) {
	tags := new([]Tag)
	err := c.Request("tags", opt, tags)
	return *tags, err
}

func (c *Client) Request(path string, opt *Filter, v interface{}) error {
	if opt == nil {
		opt = &Filter{}
	}
	if opt.OptFields == "" {
		opt.OptFields = defaultOptFields[path]
	}
	urlStr, err := addOptions(path, opt)
	if err != nil {
		return err
	}
	rel, err := url.Parse(urlStr)
	if err != nil {
		return err
	}
	u := c.BaseURL.ResolveReference(rel)
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	res := &Response{Data: v}
	err = json.NewDecoder(resp.Body).Decode(res)
	if len(res.Errors) > 0 {
		return res.Errors[0]
	}
	return err
}

func addOptions(s string, opt interface{}) (string, error) {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return s, nil
	}
	u, err := url.Parse(s)
	if err != nil {
		return s, err
	}
	qs, err := query.Values(opt)
	if err != nil {
		return s, err
	}
	u.RawQuery = qs.Encode()
	return u.String(), nil
}
<commit_msg>Default options should not modify provided options<commit_after>package asana

import (
	"encoding/json"
	"fmt"
	"github.com/google/go-querystring/query"
	"net/http"
	"net/url"
	"reflect"
)

const (
	libraryVersion = "0.1"
	userAgent      = "go-asana/" + libraryVersion
	defaultBaseURL = "https://app.asana.com/api/1.0/"
)

var defaultOptFields = map[string]string{
	"tags":       "name,color,notes",
	"users":      "name,email,photo",
	"projects":   "name,color,archived",
	"workspaces": "name,is_organization",
	"tasks":      "name,assignee,assignee_status,completed,parent",
}

type (
	Client struct {
		client    *http.Client
		BaseURL   *url.URL
		UserAgent string
	}

	Workspace struct {
		ID           int64  `json:"id,omitempty"`
		Name         string `json:"name,omitempty"`
		Organization bool   `json:"is_organization,omitempty"`
	}

	User struct {
		ID         int64       `json:"id,omitempty"`
		Email      string      `json:"email,omitempty"`
		Name       string      `json:"name,omitempty"`
		Photo      string      `json:"photo,omitempty"`
		Workspaces []Workspace `json:"workspaces,omitempty"`
	}

	Project struct {
		ID       int64  `json:"id,omitempty"`
		Name     string `json:"name,omitempty"`
		Archived bool   `json:"archived,omitempty"`
		Color    string `json:"color,omitempty"`
		Notes    string `json:"notes,omitempty"`
	}

	Task struct {
		ID             int64     `json:"id,omitempty"`
		Assignee       *User     `json:"assignee,omitempty"`
		AssigneeStatus string    `json:"assignee_status,omitempty"`
		Completed      bool      `json:"completed,omitempty"`
		Name           string    `json:"name,omitempty"`
		Notes          string    `json:"notes,omitempty"`
		ParentTask     *Task     `json:"parent,omitempty"`
		Projects       []Project `json:"projects,omitempty"`
	}

	Tag struct {
		ID    int64  `json:"id,omitempty"`
		Name  string `json:"name,omitempty"`
		Color string `json:"color,omitempty"`
		Notes string `json:"notes,omitempty"`
	}

	Filter struct {
		Archived       bool   `url:"archived,omitempty"`
		Assignee       int64  `url:"assignee,omitempty"`
		Project        int64  `url:"project,omitempty"`
		Workspace      int64  `url:"workspace,omitempty"`
		CompletedSince string `url:"completed_since,omitempty"`
		ModifiedSince  string `url:"modified_since,omitempty"`
		OptFields      string `url:"opt_fields,omitempty"`
	}

	Response struct {
		Data   interface{} `json:"data,omitempty"`
		Errors []Error     `json:"errors,omitempty"`
	}

	Error struct {
		Phrase  string `json:"phrase,omitempty"`
		Message string `json:"message,omitempty"`
	}
)

func (e Error) Error() string {
	return fmt.Sprintf("%v - %v", e.Message, e.Phrase)
}

func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(defaultBaseURL)
	client := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent}
	return client
}

func (c *Client) ListWorkspaces() ([]Workspace, error) {
	workspaces := new([]Workspace)
	err := c.Request("workspaces", nil, workspaces)
	return *workspaces, err
}

func (c *Client) ListUsers(opt *Filter) ([]User, error) {
	users := new([]User)
	err := c.Request("users", opt, users)
	return *users, err
}

func (c *Client) ListProjects(opt *Filter) ([]Project, error) {
	projects := new([]Project)
	err := c.Request("projects", opt, projects)
	return *projects, err
}

func (c *Client) ListTasks(opt *Filter) ([]Task, error) {
	tasks := new([]Task)
	err := c.Request("tasks", opt, tasks)
	return *tasks, err
}

func (c *Client) ListTags(opt *Filter) ([]Tag, error) {
	tags := new([]Tag)
	err := c.Request("tags", opt, tags)
	return *tags, err
}

func (c *Client) Request(path string, opt *Filter, v interface{}) error {
	if opt == nil {
		opt = &Filter{}
	}
	if opt.OptFields == "" {
		// default options should not modify provided options
		newOpt := *opt
		opt = &newOpt
		opt.OptFields = defaultOptFields[path]
	}
	urlStr, err := addOptions(path, opt)
	if err != nil {
		return err
	}
	rel, err := url.Parse(urlStr)
	if err != nil {
		return err
	}
	u := c.BaseURL.ResolveReference(rel)
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	res := &Response{Data: v}
	err = json.NewDecoder(resp.Body).Decode(res)
	if len(res.Errors) > 0 {
		return res.Errors[0]
	}
	return err
}

func addOptions(s string, opt interface{}) (string, error) {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return s, nil
	}
	u, err := url.Parse(s)
	if err != nil {
		return s, err
	}
	qs, err := query.Values(opt)
	if err != nil {
		return s, err
	}
	u.RawQuery = qs.Encode()
	return u.String(), nil
}
<|endoftext|>


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

// Package sdjournal provides a low-level Go interface to the
// systemd journal wrapped around the sd-journal C API.
//
// All public read methods map closely to the sd-journal API functions. See the
// sd-journal.h documentation[1] for information about each function.
//
// To write to the journal, see the pure-Go "journal" package
//
// [1] http://www.freedesktop.org/software/systemd/man/sd-journal.html
package sdjournal

/*
#cgo pkg-config: libsystemd
#include <systemd/sd-journal.h>
#include <stdlib.h>
#include <syslog.h>
*/
import "C"
import (
	"fmt"
	"path/filepath"
	"sync"
	"time"
	"unsafe"
)

// Journal entry field strings which correspond to:
// http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
const (
	SD_JOURNAL_FIELD_SYSTEMD_UNIT = "_SYSTEMD_UNIT"
	SD_JOURNAL_FIELD_MESSAGE      = "MESSAGE"
	SD_JOURNAL_FIELD_PID          = "_PID"
	SD_JOURNAL_FIELD_UID          = "_UID"
	SD_JOURNAL_FIELD_GID          = "_GID"
	SD_JOURNAL_FIELD_HOSTNAME     = "_HOSTNAME"
	SD_JOURNAL_FIELD_MACHINE_ID   = "_MACHINE_ID"
)

// Journal event constants
const (
	SD_JOURNAL_NOP        = int(C.SD_JOURNAL_NOP)
	SD_JOURNAL_APPEND     = int(C.SD_JOURNAL_APPEND)
	SD_JOURNAL_INVALIDATE = int(C.SD_JOURNAL_INVALIDATE)
)

// A Journal is a Go wrapper of an sd_journal structure.
type Journal struct {
	cjournal *C.sd_journal
	mu       sync.Mutex
}

// A Match is a convenience wrapper to describe filters supplied to AddMatch.
type Match struct {
	Field string
	Value string
}

// String returns a string representation of a Match suitable for use with AddMatch.
func (m *Match) String() string {
	return m.Field + "=" + m.Value
}

// NewJournal returns a new Journal instance pointing to the local journal
func NewJournal() (*Journal, error) {
	j := &Journal{}
	err := C.sd_journal_open(&j.cjournal, C.SD_JOURNAL_LOCAL_ONLY)

	if err < 0 {
		return nil, fmt.Errorf("failed to open journal: %s", err)
	}

	return j, nil
}

// NewJournalFromDir returns a new Journal instance pointing to a journal residing
// in a given directory. The supplied path may be relative or absolute; if
// relative, it will be converted to an absolute path before being opened.
func NewJournalFromDir(path string) (*Journal, error) {
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	p := C.CString(path)
	defer C.free(unsafe.Pointer(p))

	j := &Journal{}
	err := C.sd_journal_open_directory(&j.cjournal, p, 0)
	if err < 0 {
		return nil, fmt.Errorf("failed to open journal in directory %v: %v", path, err)
	}

	return j, nil
}

// Close closes a journal opened with NewJournal.
func (j *Journal) Close() error {
	j.mu.Lock()
	C.sd_journal_close(j.cjournal)
	j.mu.Unlock()

	return nil
}

// AddMatch adds a match by which to filter the entries of the journal.
func (j *Journal) AddMatch(match string) error {
	m := C.CString(match)
	defer C.free(unsafe.Pointer(m))

	j.mu.Lock()
	r := C.sd_journal_add_match(j.cjournal, unsafe.Pointer(m), C.size_t(len(match)))
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to add match: %d", r)
	}

	return nil
}

// AddDisjunction inserts a logical OR in the match list.
func (j *Journal) AddDisjunction() error {
	j.mu.Lock()
	r := C.sd_journal_add_disjunction(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to add a disjunction in the match list: %d", r)
	}

	return nil
}

// AddConjunction inserts a logical AND in the match list.
func (j *Journal) AddConjunction() error {
	j.mu.Lock()
	r := C.sd_journal_add_conjunction(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to add a conjunction in the match list: %d", r)
	}

	return nil
}

// FlushMatches flushes all matches, disjunctions and conjunctions.
func (j *Journal) FlushMatches() {
	j.mu.Lock()
	C.sd_journal_flush_matches(j.cjournal)
	j.mu.Unlock()
}

// Next advances the read pointer into the journal by one entry.
func (j *Journal) Next() (int, error) {
	j.mu.Lock()
	r := C.sd_journal_next(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return int(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return int(r), nil
}

// NextSkip advances the read pointer by multiple entries at once,
// as specified by the skip parameter.
func (j *Journal) NextSkip(skip uint64) (uint64, error) {
	j.mu.Lock()
	r := C.sd_journal_next_skip(j.cjournal, C.uint64_t(skip))
	j.mu.Unlock()

	if r < 0 {
		return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return uint64(r), nil
}

// Previous sets the read pointer into the journal back by one entry.
func (j *Journal) Previous() (uint64, error) {
	j.mu.Lock()
	r := C.sd_journal_previous(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return uint64(r), nil
}

// PreviousSkip sets back the read pointer by multiple entries at once,
// as specified by the skip parameter.
func (j *Journal) PreviousSkip(skip uint64) (uint64, error) {
	j.mu.Lock()
	r := C.sd_journal_previous_skip(j.cjournal, C.uint64_t(skip))
	j.mu.Unlock()

	if r < 0 {
		return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return uint64(r), nil
}

// GetData gets the data object associated with a specific field from the
// current journal entry.
func (j *Journal) GetData(field string) (string, error) {
	f := C.CString(field)
	defer C.free(unsafe.Pointer(f))

	var d unsafe.Pointer
	var l C.size_t

	j.mu.Lock()
	err := C.sd_journal_get_data(j.cjournal, f, &d, &l)
	j.mu.Unlock()

	if err < 0 {
		return "", fmt.Errorf("failed to read message: %d", err)
	}

	msg := C.GoStringN((*C.char)(d), C.int(l))

	return msg, nil
}

// SetDataThresold sets the data field size threshold for data returned by
// GetData. To retrieve the complete data fields this threshold should be
// turned off by setting it to 0, so that the library always returns the
// complete data objects.
func (j *Journal) SetDataThreshold(threshold uint64) error {
	j.mu.Lock()
	r := C.sd_journal_set_data_threshold(j.cjournal, C.size_t(threshold))
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to set data threshold: %d", r)
	}

	return nil
}

// GetRealtimeUsec gets the realtime (wallclock) timestamp of the current
// journal entry.
func (j *Journal) GetRealtimeUsec() (uint64, error) {
	var usec C.uint64_t

	j.mu.Lock()
	r := C.sd_journal_get_realtime_usec(j.cjournal, &usec)
	j.mu.Unlock()

	if r < 0 {
		return 0, fmt.Errorf("error getting timestamp for entry: %d", r)
	}

	return uint64(usec), nil
}

// SeekTail may be used to seek to the end of the journal, i.e. the most recent
// available entry.
func (j *Journal) SeekTail() error {
	j.mu.Lock()
	err := C.sd_journal_seek_tail(j.cjournal)
	j.mu.Unlock()

	if err != 0 {
		return fmt.Errorf("failed to seek to tail of journal: %s", err)
	}

	return nil
}

// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock)
// timestamp, i.e. CLOCK_REALTIME.
func (j *Journal) SeekRealtimeUsec(usec uint64) error {
	j.mu.Lock()
	err := C.sd_journal_seek_realtime_usec(j.cjournal, C.uint64_t(usec))
	j.mu.Unlock()

	if err != 0 {
		return fmt.Errorf("failed to seek to %d: %d", usec, int(err))
	}

	return nil
}

// Wait will synchronously wait until the journal gets changed. The maximum time
// this call sleeps may be controlled with the timeout parameter.
func (j *Journal) Wait(timeout time.Duration) int {
	to := uint64(time.Now().Add(timeout).Unix() / 1000)
	j.mu.Lock()
	r := C.sd_journal_wait(j.cjournal, C.uint64_t(to))
	j.mu.Unlock()

	return int(r)
}

// GetUsage returns the journal disk space usage, in bytes.
func (j *Journal) GetUsage() (uint64, error) {
	var out C.uint64_t
	j.mu.Lock()
	err := C.sd_journal_get_usage(j.cjournal, &out)
	j.mu.Unlock()

	if err != 0 {
		return 0, fmt.Errorf("failed to get journal disk space usage: %d", err)
	}

	return uint64(out), nil
}
<commit_msg>sdjournal: un-break errint->err replacement<commit_after>// Copyright 2015 RedHat, Inc.
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package sdjournal provides a low-level Go interface to the
// systemd journal wrapped around the sd-journal C API.
//
// All public read methods map closely to the sd-journal API functions. See the
// sd-journal.h documentation[1] for information about each function.
//
// To write to the journal, see the pure-Go "journal" package
//
// [1] http://www.freedesktop.org/software/systemd/man/sd-journal.html
package sdjournal

/*
#cgo pkg-config: libsystemd
#include <systemd/sd-journal.h>
#include <stdlib.h>
#include <syslog.h>
*/
import "C"
import (
	"fmt"
	"path/filepath"
	"sync"
	"time"
	"unsafe"
)

// Journal entry field strings which correspond to:
// http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
const (
	SD_JOURNAL_FIELD_SYSTEMD_UNIT = "_SYSTEMD_UNIT"
	SD_JOURNAL_FIELD_MESSAGE      = "MESSAGE"
	SD_JOURNAL_FIELD_PID          = "_PID"
	SD_JOURNAL_FIELD_UID          = "_UID"
	SD_JOURNAL_FIELD_GID          = "_GID"
	SD_JOURNAL_FIELD_HOSTNAME     = "_HOSTNAME"
	SD_JOURNAL_FIELD_MACHINE_ID   = "_MACHINE_ID"
)

// Journal event constants
const (
	SD_JOURNAL_NOP        = int(C.SD_JOURNAL_NOP)
	SD_JOURNAL_APPEND     = int(C.SD_JOURNAL_APPEND)
	SD_JOURNAL_INVALIDATE = int(C.SD_JOURNAL_INVALIDATE)
)

// A Journal is a Go wrapper of an sd_journal structure.
type Journal struct {
	cjournal *C.sd_journal
	mu       sync.Mutex
}

// A Match is a convenience wrapper to describe filters supplied to AddMatch.
type Match struct {
	Field string
	Value string
}

// String returns a string representation of a Match suitable for use with AddMatch.
func (m *Match) String() string {
	return m.Field + "=" + m.Value
}

// NewJournal returns a new Journal instance pointing to the local journal
func NewJournal() (*Journal, error) {
	j := &Journal{}
	err := C.sd_journal_open(&j.cjournal, C.SD_JOURNAL_LOCAL_ONLY)

	if err < 0 {
		return nil, fmt.Errorf("failed to open journal: %s", err)
	}

	return j, nil
}

// NewJournalFromDir returns a new Journal instance pointing to a journal residing
// in a given directory. The supplied path may be relative or absolute; if
// relative, it will be converted to an absolute path before being opened.
func NewJournalFromDir(path string) (*Journal, error) {
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	p := C.CString(path)
	defer C.free(unsafe.Pointer(p))

	j := &Journal{}
	errInt := C.sd_journal_open_directory(&j.cjournal, p, 0)
	if errInt < 0 {
		return nil, fmt.Errorf("failed to open journal in directory %v: %v", path, errInt)
	}

	return j, nil
}

// Close closes a journal opened with NewJournal.
func (j *Journal) Close() error {
	j.mu.Lock()
	C.sd_journal_close(j.cjournal)
	j.mu.Unlock()

	return nil
}

// AddMatch adds a match by which to filter the entries of the journal.
func (j *Journal) AddMatch(match string) error {
	m := C.CString(match)
	defer C.free(unsafe.Pointer(m))

	j.mu.Lock()
	r := C.sd_journal_add_match(j.cjournal, unsafe.Pointer(m), C.size_t(len(match)))
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to add match: %d", r)
	}

	return nil
}

// AddDisjunction inserts a logical OR in the match list.
func (j *Journal) AddDisjunction() error {
	j.mu.Lock()
	r := C.sd_journal_add_disjunction(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to add a disjunction in the match list: %d", r)
	}

	return nil
}

// AddConjunction inserts a logical AND in the match list.
func (j *Journal) AddConjunction() error {
	j.mu.Lock()
	r := C.sd_journal_add_conjunction(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to add a conjunction in the match list: %d", r)
	}

	return nil
}

// FlushMatches flushes all matches, disjunctions and conjunctions.
func (j *Journal) FlushMatches() {
	j.mu.Lock()
	C.sd_journal_flush_matches(j.cjournal)
	j.mu.Unlock()
}

// Next advances the read pointer into the journal by one entry.
func (j *Journal) Next() (int, error) {
	j.mu.Lock()
	r := C.sd_journal_next(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return int(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return int(r), nil
}

// NextSkip advances the read pointer by multiple entries at once,
// as specified by the skip parameter.
func (j *Journal) NextSkip(skip uint64) (uint64, error) {
	j.mu.Lock()
	r := C.sd_journal_next_skip(j.cjournal, C.uint64_t(skip))
	j.mu.Unlock()

	if r < 0 {
		return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return uint64(r), nil
}

// Previous sets the read pointer into the journal back by one entry.
func (j *Journal) Previous() (uint64, error) {
	j.mu.Lock()
	r := C.sd_journal_previous(j.cjournal)
	j.mu.Unlock()

	if r < 0 {
		return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return uint64(r), nil
}

// PreviousSkip sets back the read pointer by multiple entries at once,
// as specified by the skip parameter.
func (j *Journal) PreviousSkip(skip uint64) (uint64, error) {
	j.mu.Lock()
	r := C.sd_journal_previous_skip(j.cjournal, C.uint64_t(skip))
	j.mu.Unlock()

	if r < 0 {
		return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
	}

	return uint64(r), nil
}

// GetData gets the data object associated with a specific field from the
// current journal entry.
func (j *Journal) GetData(field string) (string, error) {
	f := C.CString(field)
	defer C.free(unsafe.Pointer(f))

	var d unsafe.Pointer
	var l C.size_t

	j.mu.Lock()
	err := C.sd_journal_get_data(j.cjournal, f, &d, &l)
	j.mu.Unlock()

	if err < 0 {
		return "", fmt.Errorf("failed to read message: %d", err)
	}

	msg := C.GoStringN((*C.char)(d), C.int(l))

	return msg, nil
}

// SetDataThresold sets the data field size threshold for data returned by
// GetData. To retrieve the complete data fields this threshold should be
// turned off by setting it to 0, so that the library always returns the
// complete data objects.
func (j *Journal) SetDataThreshold(threshold uint64) error {
	j.mu.Lock()
	r := C.sd_journal_set_data_threshold(j.cjournal, C.size_t(threshold))
	j.mu.Unlock()

	if r < 0 {
		return fmt.Errorf("failed to set data threshold: %d", r)
	}

	return nil
}

// GetRealtimeUsec gets the realtime (wallclock) timestamp of the current
// journal entry.
func (j *Journal) GetRealtimeUsec() (uint64, error) {
	var usec C.uint64_t

	j.mu.Lock()
	r := C.sd_journal_get_realtime_usec(j.cjournal, &usec)
	j.mu.Unlock()

	if r < 0 {
		return 0, fmt.Errorf("error getting timestamp for entry: %d", r)
	}

	return uint64(usec), nil
}

// SeekTail may be used to seek to the end of the journal, i.e. the most recent
// available entry.
func (j *Journal) SeekTail() error {
	j.mu.Lock()
	err := C.sd_journal_seek_tail(j.cjournal)
	j.mu.Unlock()

	if err != 0 {
		return fmt.Errorf("failed to seek to tail of journal: %s", err)
	}

	return nil
}

// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock)
// timestamp, i.e. CLOCK_REALTIME.
func (j *Journal) SeekRealtimeUsec(usec uint64) error {
	j.mu.Lock()
	err := C.sd_journal_seek_realtime_usec(j.cjournal, C.uint64_t(usec))
	j.mu.Unlock()

	if err != 0 {
		return fmt.Errorf("failed to seek to %d: %d", usec, int(err))
	}

	return nil
}

// Wait will synchronously wait until the journal gets changed. The maximum time
// this call sleeps may be controlled with the timeout parameter.
func (j *Journal) Wait(timeout time.Duration) int {
	to := uint64(time.Now().Add(timeout).Unix() / 1000)
	j.mu.Lock()
	r := C.sd_journal_wait(j.cjournal, C.uint64_t(to))
	j.mu.Unlock()

	return int(r)
}

// GetUsage returns the journal disk space usage, in bytes.
func (j *Journal) GetUsage() (uint64, error) {
	var out C.uint64_t
	j.mu.Lock()
	err := C.sd_journal_get_usage(j.cjournal, &out)
	j.mu.Unlock()

	if err != 0 {
		return 0, fmt.Errorf("failed to get journal disk space usage: %d", err)
	}

	return uint64(out), nil
}
<|endoftext|>


<commit_before>package logical

import (
	"fmt"
	"time"

	sockaddr "github.com/hashicorp/go-sockaddr"
)

type TokenType uint8

const (
	// TokenTypeDefault means "use the default, if any, that is currently set
	// on the mount". If not set, results in a Service token.
	TokenTypeDefault TokenType = iota

	// TokenTypeService is a "normal" Vault token for long-lived services
	TokenTypeService

	// TokenTypeBatch is a batch token
	TokenTypeBatch

	// TokenTypeDefaultService, configured on a mount, means that if
	// TokenTypeDefault is sent back by the mount, create Service tokens
	TokenTypeDefaultService

	// TokenTypeDefaultBatch, configured on a mount, means that if
	// TokenTypeDefault is sent back by the mount, create Batch tokens
	TokenTypeDefaultBatch
)

func (t *TokenType) UnmarshalJSON(b []byte) error {
	if len(b) == 1 {
		*t = TokenType(b[0] - '0')
		return nil
	}

	// Handle upgrade from pre-1.2 where we were serialized as string:
	s := string(b)
	switch s {
	case `"default"`, `""`:
		*t = TokenTypeDefault
	case `"service"`:
		*t = TokenTypeService
	case `"batch"`:
		*t = TokenTypeBatch
	case `"default-service"`:
		*t = TokenTypeDefaultService
	case `"default-batch"`:
		*t = TokenTypeDefaultBatch
	default:
		return fmt.Errorf("unknown token type %q", s)
	}
	return nil
}

func (t TokenType) String() string {
	switch t {
	case TokenTypeDefault:
		return "default"
	case TokenTypeService:
		return "service"
	case TokenTypeBatch:
		return "batch"
	case TokenTypeDefaultService:
		return "default-service"
	case TokenTypeDefaultBatch:
		return "default-batch"
	default:
		panic("unreachable")
	}
}

// TokenEntry is used to represent a given token
type TokenEntry struct {
	Type TokenType `json:"type" mapstructure:"type" structs:"type" sentinel:""`

	// ID of this entry, generally a random UUID
	ID string `json:"id" mapstructure:"id" structs:"id" sentinel:""`

	// Accessor for this token, a random UUID
	Accessor string `json:"accessor" mapstructure:"accessor" structs:"accessor" sentinel:""`

	// Parent token, used for revocation trees
	Parent string `json:"parent" mapstructure:"parent" structs:"parent" sentinel:""`

	// Which named policies should be used
	Policies []string `json:"policies" mapstructure:"policies" structs:"policies"`

	// InlinePolicy specifies ACL rules to be applied to this token entry.
	InlinePolicy string `json:"inline_policy" mapstructure:"inline_policy" structs:"inline_policy"`

	// Used for audit trails, this is something like "auth/user/login"
	Path string `json:"path" mapstructure:"path" structs:"path"`

	// Used for auditing. This could include things like "source", "user", "ip"
	Meta map[string]string `json:"meta" mapstructure:"meta" structs:"meta" sentinel:"meta"`

	// InternalMeta is used to store internal metadata. This metadata will not be audit logged or returned from lookup APIs.
	InternalMeta map[string]string `json:"internal_meta" mapstructure:"internal_meta" structs:"internal_meta"`

	// Used for operators to be able to associate with the source
	DisplayName string `json:"display_name" mapstructure:"display_name" structs:"display_name"`

	// Used to restrict the number of uses (zero is unlimited). This is to
	// support one-time-tokens (generalized). There are a few special values:
	// if it's -1 it has run through its use counts and is executing its final
	// use; if it's -2 it is tainted, which means revocation is currently
	// running on it; and if it's -3 it's also tainted but revocation
	// previously ran and failed, so this hints the tidy function to try it
	// again.
	NumUses int `json:"num_uses" mapstructure:"num_uses" structs:"num_uses"`

	// Time of token creation
	CreationTime int64 `json:"creation_time" mapstructure:"creation_time" structs:"creation_time" sentinel:""`

	// Duration set when token was created
	TTL time.Duration `json:"ttl" mapstructure:"ttl" structs:"ttl" sentinel:""`

	// Explicit maximum TTL on the token
	ExplicitMaxTTL time.Duration `json:"explicit_max_ttl" mapstructure:"explicit_max_ttl" structs:"explicit_max_ttl" sentinel:""`

	// If set, the role that was used for parameters at creation time
	Role string `json:"role" mapstructure:"role" structs:"role"`

	// If set, the period of the token. This is only used when created directly
	// through the create endpoint; periods managed by roles or other auth
	// backends are subject to those renewal rules.
	Period time.Duration `json:"period" mapstructure:"period" structs:"period" sentinel:""`

	// These are the deprecated fields
	DisplayNameDeprecated    string        `json:"DisplayName" mapstructure:"DisplayName" structs:"DisplayName" sentinel:""`
	NumUsesDeprecated        int           `json:"NumUses" mapstructure:"NumUses" structs:"NumUses" sentinel:""`
	CreationTimeDeprecated   int64         `json:"CreationTime" mapstructure:"CreationTime" structs:"CreationTime" sentinel:""`
	ExplicitMaxTTLDeprecated time.Duration `json:"ExplicitMaxTTL" mapstructure:"ExplicitMaxTTL" structs:"ExplicitMaxTTL" sentinel:""`

	// EntityID is the ID of the entity associated with this token.
	EntityID string `json:"entity_id" mapstructure:"entity_id" structs:"entity_id"`

	// If NoIdentityPolicies is true, the token will not inherit
	// identity policies from the associated EntityID.
	NoIdentityPolicies bool `json:"no_identity_policies" mapstructure:"no_identity_policies" structs:"no_identity_policies"`

	// The set of CIDRs that this token can be used with
	BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs" sentinel:""`

	// NamespaceID is the identifier of the namespace to which this token is
	// confined to. Do not return this value over the API when the token is
	// being looked up.
	NamespaceID string `json:"namespace_id" mapstructure:"namespace_id" structs:"namespace_id" sentinel:""`

	// CubbyholeID is the identifier of the cubbyhole storage belonging to this
	// token
	CubbyholeID string `json:"cubbyhole_id" mapstructure:"cubbyhole_id" structs:"cubbyhole_id" sentinel:""`
}

func (te *TokenEntry) SentinelGet(key string) (interface{}, error) {
	if te == nil {
		return nil, nil
	}
	switch key {
	case "policies":
		return te.Policies, nil

	case "path":
		return te.Path, nil

	case "display_name":
		return te.DisplayName, nil

	case "num_uses":
		return te.NumUses, nil

	case "role":
		return te.Role, nil

	case "entity_id":
		return te.EntityID, nil

	case "period":
		return te.Period, nil

	case "period_seconds":
		return int64(te.Period.Seconds()), nil

	case "explicit_max_ttl":
		return te.ExplicitMaxTTL, nil

	case "explicit_max_ttl_seconds":
		return int64(te.ExplicitMaxTTL.Seconds()), nil

	case "creation_ttl":
		return te.TTL, nil

	case "creation_ttl_seconds":
		return int64(te.TTL.Seconds()), nil

	case "creation_time":
		return time.Unix(te.CreationTime, 0).Format(time.RFC3339Nano), nil

	case "creation_time_unix":
		return time.Unix(te.CreationTime, 0), nil

	case "meta", "metadata":
		return te.Meta, nil

	case "type":
		teType := te.Type
		switch teType {
		case TokenTypeBatch, TokenTypeService:
		case TokenTypeDefault:
			teType = TokenTypeService
		default:
			return "unknown", nil
		}
		return teType.String(), nil
	}

	return nil, nil
}

func (te *TokenEntry) SentinelKeys() []string {
	return []string{
		"period",
		"period_seconds",
		"explicit_max_ttl",
		"explicit_max_ttl_seconds",
		"creation_ttl",
		"creation_ttl_seconds",
		"creation_time",
		"creation_time_unix",
		"meta",
		"metadata",
		"type",
	}
}
<commit_msg>Add Root Token check for TokenEntry (#12835)<commit_after>package logical

import (
	"fmt"
	"time"

	sockaddr "github.com/hashicorp/go-sockaddr"
)

type TokenType uint8

const (
	// TokenTypeDefault means "use the default, if any, that is currently set
	// on the mount". If not set, results in a Service token.
	TokenTypeDefault TokenType = iota

	// TokenTypeService is a "normal" Vault token for long-lived services
	TokenTypeService

	// TokenTypeBatch is a batch token
	TokenTypeBatch

	// TokenTypeDefaultService, configured on a mount, means that if
	// TokenTypeDefault is sent back by the mount, create Service tokens
	TokenTypeDefaultService

	// TokenTypeDefaultBatch, configured on a mount, means that if
	// TokenTypeDefault is sent back by the mount, create Batch tokens
	TokenTypeDefaultBatch
)

func (t *TokenType) UnmarshalJSON(b []byte) error {
	if len(b) == 1 {
		*t = TokenType(b[0] - '0')
		return nil
	}

	// Handle upgrade from pre-1.2 where we were serialized as string:
	s := string(b)
	switch s {
	case `"default"`, `""`:
		*t = TokenTypeDefault
	case `"service"`:
		*t = TokenTypeService
	case `"batch"`:
		*t = TokenTypeBatch
	case `"default-service"`:
		*t = TokenTypeDefaultService
	case `"default-batch"`:
		*t = TokenTypeDefaultBatch
	default:
		return fmt.Errorf("unknown token type %q", s)
	}
	return nil
}

func (t TokenType) String() string {
	switch t {
	case TokenTypeDefault:
		return "default"
	case TokenTypeService:
		return "service"
	case TokenTypeBatch:
		return "batch"
	case TokenTypeDefaultService:
		return "default-service"
	case TokenTypeDefaultBatch:
		return "default-batch"
	default:
		panic("unreachable")
	}
}

// TokenEntry is used to represent a given token
type TokenEntry struct {
	Type TokenType `json:"type" mapstructure:"type" structs:"type" sentinel:""`

	// ID of this entry, generally a random UUID
	ID string `json:"id" mapstructure:"id" structs:"id" sentinel:""`

	// Accessor for this token, a random UUID
	Accessor string `json:"accessor" mapstructure:"accessor" structs:"accessor" sentinel:""`

	// Parent token, used for revocation trees
	Parent string `json:"parent" mapstructure:"parent" structs:"parent" sentinel:""`

	// Which named policies should be used
	Policies []string `json:"policies" mapstructure:"policies" structs:"policies"`

	// InlinePolicy specifies ACL rules to be applied to this token entry.
	InlinePolicy string `json:"inline_policy" mapstructure:"inline_policy" structs:"inline_policy"`

	// Used for audit trails, this is something like "auth/user/login"
	Path string `json:"path" mapstructure:"path" structs:"path"`

	// Used for auditing. This could include things like "source", "user", "ip"
	Meta map[string]string `json:"meta" mapstructure:"meta" structs:"meta" sentinel:"meta"`

	// InternalMeta is used to store internal metadata. This metadata will not be audit logged or returned from lookup APIs.
	InternalMeta map[string]string `json:"internal_meta" mapstructure:"internal_meta" structs:"internal_meta"`

	// Used for operators to be able to associate with the source
	DisplayName string `json:"display_name" mapstructure:"display_name" structs:"display_name"`

	// Used to restrict the number of uses (zero is unlimited). This is to
	// support one-time-tokens (generalized). There are a few special values:
	// if it's -1 it has run through its use counts and is executing its final
	// use; if it's -2 it is tainted, which means revocation is currently
	// running on it; and if it's -3 it's also tainted but revocation
	// previously ran and failed, so this hints the tidy function to try it
	// again.
	NumUses int `json:"num_uses" mapstructure:"num_uses" structs:"num_uses"`

	// Time of token creation
	CreationTime int64 `json:"creation_time" mapstructure:"creation_time" structs:"creation_time" sentinel:""`

	// Duration set when token was created
	TTL time.Duration `json:"ttl" mapstructure:"ttl" structs:"ttl" sentinel:""`

	// Explicit maximum TTL on the token
	ExplicitMaxTTL time.Duration `json:"explicit_max_ttl" mapstructure:"explicit_max_ttl" structs:"explicit_max_ttl" sentinel:""`

	// If set, the role that was used for parameters at creation time
	Role string `json:"role" mapstructure:"role" structs:"role"`

	// If set, the period of the token. This is only used when created directly
	// through the create endpoint; periods managed by roles or other auth
	// backends are subject to those renewal rules.
	Period time.Duration `json:"period" mapstructure:"period" structs:"period" sentinel:""`

	// These are the deprecated fields
	DisplayNameDeprecated    string        `json:"DisplayName" mapstructure:"DisplayName" structs:"DisplayName" sentinel:""`
	NumUsesDeprecated        int           `json:"NumUses" mapstructure:"NumUses" structs:"NumUses" sentinel:""`
	CreationTimeDeprecated   int64         `json:"CreationTime" mapstructure:"CreationTime" structs:"CreationTime" sentinel:""`
	ExplicitMaxTTLDeprecated time.Duration `json:"ExplicitMaxTTL" mapstructure:"ExplicitMaxTTL" structs:"ExplicitMaxTTL" sentinel:""`

	// EntityID is the ID of the entity associated with this token.
	EntityID string `json:"entity_id" mapstructure:"entity_id" structs:"entity_id"`

	// If NoIdentityPolicies is true, the token will not inherit
	// identity policies from the associated EntityID.
	NoIdentityPolicies bool `json:"no_identity_policies" mapstructure:"no_identity_policies" structs:"no_identity_policies"`

	// The set of CIDRs that this token can be used with
	BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs" sentinel:""`

	// NamespaceID is the identifier of the namespace to which this token is
	// confined to. Do not return this value over the API when the token is
	// being looked up.
	NamespaceID string `json:"namespace_id" mapstructure:"namespace_id" structs:"namespace_id" sentinel:""`

	// CubbyholeID is the identifier of the cubbyhole storage belonging to this
	// token
	CubbyholeID string `json:"cubbyhole_id" mapstructure:"cubbyhole_id" structs:"cubbyhole_id" sentinel:""`
}

func (te *TokenEntry) SentinelGet(key string) (interface{}, error) {
	if te == nil {
		return nil, nil
	}
	switch key {
	case "policies":
		return te.Policies, nil

	case "path":
		return te.Path, nil

	case "display_name":
		return te.DisplayName, nil

	case "num_uses":
		return te.NumUses, nil

	case "role":
		return te.Role, nil

	case "entity_id":
		return te.EntityID, nil

	case "period":
		return te.Period, nil

	case "period_seconds":
		return int64(te.Period.Seconds()), nil

	case "explicit_max_ttl":
		return te.ExplicitMaxTTL, nil

	case "explicit_max_ttl_seconds":
		return int64(te.ExplicitMaxTTL.Seconds()), nil

	case "creation_ttl":
		return te.TTL, nil

	case "creation_ttl_seconds":
		return int64(te.TTL.Seconds()), nil

	case "creation_time":
		return time.Unix(te.CreationTime, 0).Format(time.RFC3339Nano), nil

	case "creation_time_unix":
		return time.Unix(te.CreationTime, 0), nil

	case "meta", "metadata":
		return te.Meta, nil

	case "type":
		teType := te.Type
		switch teType {
		case TokenTypeBatch, TokenTypeService:
		case TokenTypeDefault:
			teType = TokenTypeService
		default:
			return "unknown", nil
		}
		return teType.String(), nil
	}

	return nil, nil
}

func (te *TokenEntry) SentinelKeys() []string {
	return []string{
		"period",
		"period_seconds",
		"explicit_max_ttl",
		"explicit_max_ttl_seconds",
		"creation_ttl",
		"creation_ttl_seconds",
		"creation_time",
		"creation_time_unix",
		"meta",
		"metadata",
		"type",
	}
}

// IsRoot returns false if the token is not root (or doesn't exist)
func (te *TokenEntry) IsRoot() bool {
	if te == nil {
		return false
	}

	return len(te.Policies) == 1 && te.Policies[0] == "root"
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import()

func (p *Position) quiescence(depth, ply int, alpha, beta int) int {
        Log("\nquiescence(depth: %d/%d, color: %s, alpha: %d, beta: %d)\n", depth, ply, C(p.color), alpha, beta)
        p.game.qnodes++

        if p.isRepetition() {
                return 0
        }

        if ply > MaxPly - 2 {
                return p.Evaluate()
        }

	// Checkmate pruning.
	if Checkmate - ply <= alpha {
		return alpha
	} else if -Checkmate + ply >= beta {
		return beta
	}

        if p.inCheck {
                return p.quiescenceInCheck(depth, ply, alpha, beta)
        }
        return p.quiescenceStayPat(depth, ply, alpha, beta)
}

func (p *Position) quiescenceInCheck(depth, ply int, alpha, beta int) int {
        score, bestScore := 0, -Checkmate
        quietAlpha, quietBeta := alpha, beta

        gen := p.StartMoveGen(ply).GenerateMoves()
        movesMade := 0
        for move := gen.NextMove(); move != 0; move = gen.NextMove() {
                if position := p.MakeMove(move); position != nil {
                        movesMade++
                        Log("%d: evasion %s for %s\n", movesMade, move, C(move.color()))

                        score = -position.quiescence(depth - 1, ply + 1, -quietBeta, -quietAlpha)
                        if alpha + 1 != beta && score > quietAlpha && quietAlpha + 1 == quietBeta {
                                score = -position.quiescence(depth - 1, ply + 1, -beta, -quietAlpha)
                        }
                        position.TakeBack(move)

                        if score >= beta {
                                return score
                        }
                        if score > bestScore {
                                bestScore = score
                                if score > quietAlpha {
                                        quietAlpha = score
                                        p.saveBest(ply, move)
                                }
                        }
                        quietBeta = quietAlpha + 1
                }
        }

        if movesMade == 0 {
                p.game.bestLength[ply] = ply
                return -Checkmate + ply
        }

        Log("End of quiescenceInCheck(depth: %d/%d, color: %s, alpha: %d, beta: %d) => %d\n", depth, ply, C(p.color), alpha, beta, alpha)
        return bestScore
}

func (p *Position) quiescenceStayPat(depth, ply int, alpha, beta int) int {
        score := p.Evaluate()
        if score >= beta {
                return score
        }

        bestScore, quietAlpha, quietBeta := score, alpha, beta
        if score > alpha {
                p.game.bestLength[ply] = ply
                quietAlpha = score
        }

        gen := p.StartMoveGen(ply).GenerateCaptures() // TODO: followed by quiet checks.
        movesMade := 0
        for move := gen.NextMove(); move != 0; move = gen.NextMove() {
                if position := p.MakeMove(move); position != nil {
                        movesMade++
                        Log("%d: capture %s for %s\n", movesMade, move, C(move.color()))

                        score = -position.quiescence(depth - 1, ply + 1, -quietBeta, -quietAlpha)
                        if quietAlpha + 1 != beta && score > quietAlpha && quietAlpha + 1 == quietBeta {
                                score = -position.quiescence(depth - 1, ply + 1, -beta, -quietAlpha)
                        }
                        position.TakeBack(move)

                        if score >= beta {
                                return score
                        }
                        if score > bestScore {
                                bestScore = score
                                if score > quietAlpha {
                                        quietAlpha = score
                                        p.saveBest(ply, move)
                                }
                        }
                        quietBeta = quietAlpha + 1
                }
        }

        Log("End of quiescenceStayPat(depth: %d/%d, color: %s, alpha: %d, beta: %d) => %d\n", depth, ply, C(p.color), alpha, beta, alpha)
        return bestScore
}
<commit_msg>Use evasion generator in quiescence search<commit_after>// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import()

func (p *Position) quiescence(depth, ply int, alpha, beta int) int {
        Log("\nquiescence(depth: %d/%d, color: %s, alpha: %d, beta: %d)\n", depth, ply, C(p.color), alpha, beta)
        p.game.qnodes++

        if p.isRepetition() {
                return 0
        }

        if ply > MaxPly - 2 {
                return p.Evaluate()
        }

	// Checkmate pruning.
	if Checkmate - ply <= alpha {
		return alpha
	} else if -Checkmate + ply >= beta {
		return beta
	}

        if p.inCheck {
                return p.quiescenceInCheck(depth, ply, alpha, beta)
        }
        return p.quiescenceStayPat(depth, ply, alpha, beta)
}

func (p *Position) quiescenceInCheck(depth, ply int, alpha, beta int) int {
        score, bestScore := 0, -Checkmate
        quietAlpha, quietBeta := alpha, beta

        gen := p.StartMoveGen(ply).GenerateEvasions()
        movesMade := 0
        for move := gen.NextMove(); move != 0; move = gen.NextMove() {
                if position := p.MakeMove(move); position != nil {
                        movesMade++
                        Log("%d: evasion %s for %s\n", movesMade, move, C(move.color()))

                        score = -position.quiescence(depth - 1, ply + 1, -quietBeta, -quietAlpha)
                        if alpha + 1 != beta && score > quietAlpha && quietAlpha + 1 == quietBeta {
                                score = -position.quiescence(depth - 1, ply + 1, -beta, -quietAlpha)
                        }
                        position.TakeBack(move)

                        if score >= beta {
                                return score
                        }
                        if score > bestScore {
                                bestScore = score
                                if score > quietAlpha {
                                        quietAlpha = score
                                        p.saveBest(ply, move)
                                }
                        }
                        quietBeta = quietAlpha + 1
                }
        }

        if movesMade == 0 {
                p.game.bestLength[ply] = ply
                return -Checkmate + ply
        }

        Log("End of quiescenceInCheck(depth: %d/%d, color: %s, alpha: %d, beta: %d) => %d\n", depth, ply, C(p.color), alpha, beta, alpha)
        return bestScore
}

func (p *Position) quiescenceStayPat(depth, ply int, alpha, beta int) int {
        score := p.Evaluate()
        if score >= beta {
                return score
        }

        bestScore, quietAlpha, quietBeta := score, alpha, beta
        if score > alpha {
                p.game.bestLength[ply] = ply
                quietAlpha = score
        }

        gen := p.StartMoveGen(ply).GenerateCaptures() // TODO: followed by quiet checks.
        movesMade := 0
        for move := gen.NextMove(); move != 0; move = gen.NextMove() {
                if position := p.MakeMove(move); position != nil {
                        movesMade++
                        Log("%d: capture %s for %s\n", movesMade, move, C(move.color()))

                        score = -position.quiescence(depth - 1, ply + 1, -quietBeta, -quietAlpha)
                        if quietAlpha + 1 != beta && score > quietAlpha && quietAlpha + 1 == quietBeta {
                                score = -position.quiescence(depth - 1, ply + 1, -beta, -quietAlpha)
                        }
                        position.TakeBack(move)

                        if score >= beta {
                                return score
                        }
                        if score > bestScore {
                                bestScore = score
                                if score > quietAlpha {
                                        quietAlpha = score
                                        p.saveBest(ply, move)
                                }
                        }
                        quietBeta = quietAlpha + 1
                }
        }

        Log("End of quiescenceStayPat(depth: %d/%d, color: %s, alpha: %d, beta: %d) => %d\n", depth, ply, C(p.color), alpha, beta, alpha)
        return bestScore
}
<|endoftext|>