<commit_before>// Copyright 2016 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"math/rand"
	"net/http"
	"path"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/embed"
	"github.com/coreos/etcd/pkg/types"
	"github.com/juju/errors"
	"github.com/ngaut/systimemon"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"github.com/pingcap/pd/pkg/etcdutil"
	"google.golang.org/grpc"
)

const (
	etcdTimeout = time.Second * 3
	// pdRootPath for all pd servers.
	pdRootPath      = "/pd"
	pdAPIPrefix     = "/pd/"
	pdRPCPrefix     = "/pd/rpc"
	pdClusterIDPath = "/pd/cluster_id"
)

// Server is the pd server.
type Server struct {
	cfg         *Config
	scheduleOpt *scheduleOption

	etcd *embed.Etcd

	client *clientv3.Client

	clusterID uint64

	rootPath string

	isLeaderValue int64
	// leader value saved in etcd leader key.
	// Every write will use this to check leader validation.
	leaderValue string

	wg sync.WaitGroup

	closed int64

	// for tso
	ts            atomic.Value
	lastSavedTime time.Time

	// for id allocator, we can use one allocator for
	// store, region and peer, because we just need
	// a unique ID.
	idAlloc *idAllocator

	// for kv operation.
	kv *kv

	// for API operation.
	handler *Handler

	// for raft cluster
	clusterLock sync.RWMutex
	cluster     *RaftCluster

	msgID uint64

	id uint64
}

// NewServer creates the pd server with given configuration.
func NewServer(cfg *Config) (*Server, error) {
	s := CreateServer(cfg)
	if err := s.StartEtcd(nil); err != nil {
		s.Close()
		return nil, errors.Trace(err)
	}

	go systimemon.StartMonitor(time.Now, func() {
		log.Errorf("system time jumps backward")
		timeJumpBackCounter.Inc()
	})
	return s, nil
}

// CreateServer creates the UNINITIALIZED pd server with given configuration.
func CreateServer(cfg *Config) *Server {
	log.Infof("PD config - %v", cfg)
	rand.Seed(time.Now().UnixNano())

	s := &Server{
		cfg:           cfg,
		scheduleOpt:   newScheduleOption(cfg),
		isLeaderValue: 0,
		closed:        1,
	}

	s.handler = newHandler(s)
	return s
}

// StartEtcd starts an embed etcd server with an user handler.
func (s *Server) StartEtcd(apiHandler http.Handler) error {
	etcdCfg, err := s.cfg.genEmbedEtcdConfig()
	if err != nil {
		return errors.Trace(err)
	}
	if apiHandler != nil {
		etcdCfg.UserHandlers = map[string]http.Handler{
			pdAPIPrefix: apiHandler,
		}
	}
	etcdCfg.ServiceRegister = func(gs *grpc.Server) { pdpb.RegisterPDServer(gs, s) }

	log.Info("start embed etcd")

	etcd, err := embed.StartEtcd(etcdCfg)
	if err != nil {
		return errors.Trace(err)
	}

	// Check cluster ID
	urlmap, err := types.NewURLsMap(s.cfg.InitialCluster)
	if err != nil {
		return errors.Trace(err)
	}
	if err = etcdutil.CheckClusterID(etcd.Server.Cluster().ID(), urlmap); err != nil {
		return errors.Trace(err)
	}

	endpoints := []string{etcdCfg.LCUrls[0].String()}

	log.Infof("create etcd v3 client with endpoints %v", endpoints)
	client, err := clientv3.New(clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: etcdTimeout,
	})
	if err != nil {
		return errors.Trace(err)
	}

	if err = etcdutil.WaitEtcdStart(client, endpoints[0]); err != nil {
		// See https://github.com/coreos/etcd/issues/6067
		// Here may return "not capable" error because we don't start
		// all etcds in initial_cluster at same time, so here just log
		// an error.
		// Note that pd can not work correctly if we don't start all etcds.
		log.Errorf("etcd start failed, err %v", err)
	}

	s.etcd = etcd
	s.client = client
	s.id = uint64(etcd.Server.ID())

	// update advertise peer urls.
	etcdMembers, err := etcdutil.ListEtcdMembers(client)
	if err != nil {
		return errors.Trace(err)
	}
	for _, m := range etcdMembers.Members {
		if s.ID() == m.ID {
			etcdPeerURLs := strings.Join(m.PeerURLs, ",")
			if s.cfg.AdvertisePeerUrls != etcdPeerURLs {
				log.Infof("update advertise peer urls from %s to %s", s.cfg.AdvertisePeerUrls, etcdPeerURLs)
				s.cfg.AdvertisePeerUrls = etcdPeerURLs
			}
		}
	}

	if err = s.initClusterID(); err != nil {
		return errors.Trace(err)
	}
	log.Infof("init cluster id %v", s.clusterID)

	s.rootPath = path.Join(pdRootPath, strconv.FormatUint(s.clusterID, 10))
	s.idAlloc = &idAllocator{s: s}
	s.kv = newKV(s)
	s.cluster = newRaftCluster(s, s.clusterID)

	// Server has started.
	atomic.StoreInt64(&s.closed, 0)
	return nil
}

func (s *Server) initClusterID() error {
	// Get any cluster key to parse the cluster ID.
	resp, err := kvGet(s.client, pdRootPath, clientv3.WithFirstCreate()...)
	if err != nil {
		return errors.Trace(err)
	}

	// If no key exist, generate a random cluster ID.
	if len(resp.Kvs) == 0 {
		s.clusterID, err = initOrGetClusterID(s.client, pdClusterIDPath)
		return errors.Trace(err)
	}

	key := string(resp.Kvs[0].Key)

	// If the key is "pdClusterIDPath", parse the cluster ID from it.
	if key == pdClusterIDPath {
		s.clusterID, err = bytesToUint64(resp.Kvs[0].Value)
		return errors.Trace(err)
	}

	// Parse the cluster ID from any other keys for compatibility.
	elems := strings.Split(key, "/")
	if len(elems) < 3 {
		return errors.Errorf("invalid cluster key %v", key)
	}
	s.clusterID, err = strconv.ParseUint(elems[2], 10, 64)

	log.Infof("init and load cluster id: %d", s.clusterID)
	return errors.Trace(err)
}

// Close closes the server.
func (s *Server) Close() {
	if !atomic.CompareAndSwapInt64(&s.closed, 0, 1) {
		// server is already closed
		return
	}

	log.Info("closing server")

	s.enableLeader(false)

	if s.client != nil {
		s.client.Close()
	}

	if s.etcd != nil {
		s.etcd.Close()
	}

	s.wg.Wait()

	log.Info("close server")
}

// isClosed checks whether server is closed or not.
func (s *Server) isClosed() bool {
	return atomic.LoadInt64(&s.closed) == 1
}

// Run runs the pd server.
func (s *Server) Run() {
	// We use "127.0.0.1:0" for test and will set correct listening
	// address before run, so we set leader value here.
	s.leaderValue = s.marshalLeader()

	s.wg.Add(1)
	s.leaderLoop()
}

// GetAddr returns the server urls for clients.
func (s *Server) GetAddr() string {
	return s.cfg.AdvertiseClientUrls
}

// GetHandler returns the handler for API.
func (s *Server) GetHandler() *Handler {
	return s.handler
}

// GetEndpoints returns the etcd endpoints for outer use.
func (s *Server) GetEndpoints() []string {
	return s.client.Endpoints()
}

// GetClient returns builtin etcd client.
func (s *Server) GetClient() *clientv3.Client {
	return s.client
}

// ID returns the unique etcd ID for this server in etcd cluster.
func (s *Server) ID() uint64 {
	return s.id
}

// Name returns the unique etcd Name for this server in etcd cluster.
func (s *Server) Name() string {
	return s.cfg.Name
}

// ClusterID returns the cluster ID of this server.
func (s *Server) ClusterID() uint64 {
	return s.clusterID
}

// txn returns an etcd client transaction wrapper.
// The wrapper will set a request timeout to the context and log slow transactions.
func (s *Server) txn() clientv3.Txn {
	return newSlowLogTxn(s.client)
}

// leaderTxn returns txn() with a leader comparison to guarantee that
// the transaction can be executed only if the server is leader.
func (s *Server) leaderTxn(cs ...clientv3.Cmp) clientv3.Txn {
	return s.txn().If(append(cs, s.leaderCmp())...)
}
<commit_msg>server: fix dial addr bug. (#642)<commit_after>// Copyright 2016 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"math/rand"
	"net/http"
	"path"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/embed"
	"github.com/coreos/etcd/pkg/types"
	"github.com/juju/errors"
	"github.com/ngaut/systimemon"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"github.com/pingcap/pd/pkg/etcdutil"
	"google.golang.org/grpc"
)

const (
	etcdTimeout = time.Second * 3
	// pdRootPath for all pd servers.
	pdRootPath      = "/pd"
	pdAPIPrefix     = "/pd/"
	pdRPCPrefix     = "/pd/rpc"
	pdClusterIDPath = "/pd/cluster_id"
)

// Server is the pd server.
type Server struct {
	cfg         *Config
	scheduleOpt *scheduleOption

	etcd *embed.Etcd

	client *clientv3.Client

	clusterID uint64

	rootPath string

	isLeaderValue int64
	// leader value saved in etcd leader key.
	// Every write will use this to check leader validation.
	leaderValue string

	wg sync.WaitGroup

	closed int64

	// for tso
	ts            atomic.Value
	lastSavedTime time.Time

	// for id allocator, we can use one allocator for
	// store, region and peer, because we just need
	// a unique ID.
	idAlloc *idAllocator

	// for kv operation.
	kv *kv

	// for API operation.
	handler *Handler

	// for raft cluster
	clusterLock sync.RWMutex
	cluster     *RaftCluster

	msgID uint64

	id uint64
}

// NewServer creates the pd server with given configuration.
func NewServer(cfg *Config) (*Server, error) {
	s := CreateServer(cfg)
	if err := s.StartEtcd(nil); err != nil {
		s.Close()
		return nil, errors.Trace(err)
	}

	go systimemon.StartMonitor(time.Now, func() {
		log.Errorf("system time jumps backward")
		timeJumpBackCounter.Inc()
	})
	return s, nil
}

// CreateServer creates the UNINITIALIZED pd server with given configuration.
func CreateServer(cfg *Config) *Server {
	log.Infof("PD config - %v", cfg)
	rand.Seed(time.Now().UnixNano())

	s := &Server{
		cfg:           cfg,
		scheduleOpt:   newScheduleOption(cfg),
		isLeaderValue: 0,
		closed:        1,
	}

	s.handler = newHandler(s)
	return s
}

// StartEtcd starts an embed etcd server with an user handler.
func (s *Server) StartEtcd(apiHandler http.Handler) error {
	etcdCfg, err := s.cfg.genEmbedEtcdConfig()
	if err != nil {
		return errors.Trace(err)
	}
	if apiHandler != nil {
		etcdCfg.UserHandlers = map[string]http.Handler{
			pdAPIPrefix: apiHandler,
		}
	}
	etcdCfg.ServiceRegister = func(gs *grpc.Server) { pdpb.RegisterPDServer(gs, s) }

	log.Info("start embed etcd")

	etcd, err := embed.StartEtcd(etcdCfg)
	if err != nil {
		return errors.Trace(err)
	}

	// Check cluster ID
	urlmap, err := types.NewURLsMap(s.cfg.InitialCluster)
	if err != nil {
		return errors.Trace(err)
	}
	if err = etcdutil.CheckClusterID(etcd.Server.Cluster().ID(), urlmap); err != nil {
		return errors.Trace(err)
	}

	endpoints := []string{etcdCfg.ACUrls[0].String()}

	log.Infof("create etcd v3 client with endpoints %v", endpoints)
	client, err := clientv3.New(clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: etcdTimeout,
	})
	if err != nil {
		return errors.Trace(err)
	}

	if err = etcdutil.WaitEtcdStart(client, endpoints[0]); err != nil {
		// See https://github.com/coreos/etcd/issues/6067
		// Here may return "not capable" error because we don't start
		// all etcds in initial_cluster at same time, so here just log
		// an error.
		// Note that pd can not work correctly if we don't start all etcds.
		log.Errorf("etcd start failed, err %v", err)
	}

	s.etcd = etcd
	s.client = client
	s.id = uint64(etcd.Server.ID())

	// update advertise peer urls.
	etcdMembers, err := etcdutil.ListEtcdMembers(client)
	if err != nil {
		return errors.Trace(err)
	}
	for _, m := range etcdMembers.Members {
		if s.ID() == m.ID {
			etcdPeerURLs := strings.Join(m.PeerURLs, ",")
			if s.cfg.AdvertisePeerUrls != etcdPeerURLs {
				log.Infof("update advertise peer urls from %s to %s", s.cfg.AdvertisePeerUrls, etcdPeerURLs)
				s.cfg.AdvertisePeerUrls = etcdPeerURLs
			}
		}
	}

	if err = s.initClusterID(); err != nil {
		return errors.Trace(err)
	}
	log.Infof("init cluster id %v", s.clusterID)

	s.rootPath = path.Join(pdRootPath, strconv.FormatUint(s.clusterID, 10))
	s.idAlloc = &idAllocator{s: s}
	s.kv = newKV(s)
	s.cluster = newRaftCluster(s, s.clusterID)

	// Server has started.
	atomic.StoreInt64(&s.closed, 0)
	return nil
}

func (s *Server) initClusterID() error {
	// Get any cluster key to parse the cluster ID.
	resp, err := kvGet(s.client, pdRootPath, clientv3.WithFirstCreate()...)
	if err != nil {
		return errors.Trace(err)
	}

	// If no key exist, generate a random cluster ID.
	if len(resp.Kvs) == 0 {
		s.clusterID, err = initOrGetClusterID(s.client, pdClusterIDPath)
		return errors.Trace(err)
	}

	key := string(resp.Kvs[0].Key)

	// If the key is "pdClusterIDPath", parse the cluster ID from it.
	if key == pdClusterIDPath {
		s.clusterID, err = bytesToUint64(resp.Kvs[0].Value)
		return errors.Trace(err)
	}

	// Parse the cluster ID from any other keys for compatibility.
	elems := strings.Split(key, "/")
	if len(elems) < 3 {
		return errors.Errorf("invalid cluster key %v", key)
	}
	s.clusterID, err = strconv.ParseUint(elems[2], 10, 64)

	log.Infof("init and load cluster id: %d", s.clusterID)
	return errors.Trace(err)
}

// Close closes the server.
func (s *Server) Close() {
	if !atomic.CompareAndSwapInt64(&s.closed, 0, 1) {
		// server is already closed
		return
	}

	log.Info("closing server")

	s.enableLeader(false)

	if s.client != nil {
		s.client.Close()
	}

	if s.etcd != nil {
		s.etcd.Close()
	}

	s.wg.Wait()

	log.Info("close server")
}

// isClosed checks whether server is closed or not.
func (s *Server) isClosed() bool {
	return atomic.LoadInt64(&s.closed) == 1
}

// Run runs the pd server.
func (s *Server) Run() {
	// We use "127.0.0.1:0" for test and will set correct listening
	// address before run, so we set leader value here.
	s.leaderValue = s.marshalLeader()

	s.wg.Add(1)
	s.leaderLoop()
}

// GetAddr returns the server urls for clients.
func (s *Server) GetAddr() string {
	return s.cfg.AdvertiseClientUrls
}

// GetHandler returns the handler for API.
func (s *Server) GetHandler() *Handler {
	return s.handler
}

// GetEndpoints returns the etcd endpoints for outer use.
func (s *Server) GetEndpoints() []string {
	return s.client.Endpoints()
}

// GetClient returns builtin etcd client.
func (s *Server) GetClient() *clientv3.Client {
	return s.client
}

// ID returns the unique etcd ID for this server in etcd cluster.
func (s *Server) ID() uint64 {
	return s.id
}

// Name returns the unique etcd Name for this server in etcd cluster.
func (s *Server) Name() string {
	return s.cfg.Name
}

// ClusterID returns the cluster ID of this server.
func (s *Server) ClusterID() uint64 {
	return s.clusterID
}

// txn returns an etcd client transaction wrapper.
// The wrapper will set a request timeout to the context and log slow transactions.
func (s *Server) txn() clientv3.Txn {
	return newSlowLogTxn(s.client)
}

// leaderTxn returns txn() with a leader comparison to guarantee that
// the transaction can be executed only if the server is leader.
func (s *Server) leaderTxn(cs ...clientv3.Cmp) clientv3.Txn {
	return s.txn().If(append(cs, s.leaderCmp())...)
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Air Computing Inc. <oss@aerofs.com>
// All rights reserved.

package server

import (
	"crypto/tls"
	"fmt"
	"github.com/aerofs/lipwig/ssmp"
	"io"
	"net"
	"sync"
)

// A ConnectionManager manages a set of Connection.
// All methods are safe to call from multiple goroutines simultaneously.
type ConnectionManager struct {
	connection  sync.Mutex
	anonymous   map[*Connection]*Connection
	connections map[string]*Connection
}

// A TopicManager manages a set of Topic.
// All methods are safe to call from multiple goroutines simultaneously.
type TopicManager struct {
	topic  sync.Mutex
	topics map[string]*Topic
}

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

// server implements Server, ConnectionManager and TopicManager
type Server struct {
	ConnectionManager
	TopicManager

	l    *net.TCPListener
	cfg  *tls.Config
	auth Authenticator

	// used to cleanly Stop the goroutine spawned by Start
	w sync.WaitGroup

	dispatcher *Dispatcher
}

// NewServer creates a new SSMP server from a TCP Listener, an Authenticator
// and a TLS configuration.
func NewServer(l net.Listener, auth Authenticator, cfg *tls.Config) *Server {
	s := &Server{
		l:    l.(*net.TCPListener),
		cfg:  cfg,
		auth: auth,
		ConnectionManager: ConnectionManager{
			anonymous:   make(map[*Connection]*Connection),
			connections: make(map[string]*Connection),
		},
		TopicManager: TopicManager{
			topics: make(map[string]*Topic),
		},
	}
	s.dispatcher = NewDispatcher(&s.TopicManager, &s.ConnectionManager)
	return s
}

// Serve accept connections in the calling goroutine and only returns
// in case of error.
func (s *Server) Serve() error {
	s.w.Add(1)
	return s.serve()
}

// Start accepts connection in a new goroutine and returns the Server
// This allows the following terse idiom:
//		defer s.Start().Stop()
func (s *Server) Start() *Server {
	s.w.Add(1)
	go s.serve()
	return s
}

// ListeningPort returns the TCP port to which the underlying Listener is bound.
func (s *Server) ListeningPort() int {
	return s.l.Addr().(*net.TCPAddr).Port
}

// Stop stops accepting new connections and immediately closes all existing
// connections. Serve
func (s *Server) Stop() {
	s.l.Close()
	s.connection.Lock()
	for _, c := range s.connections {
		c.Close()
	}
	for c := range s.anonymous {
		c.Close()
	}
	s.connection.Unlock()
	s.w.Wait()
}

// DumpStats writes some internal stats to the given Writer.
func (s *Server) DumpStats(w io.Writer) {
	io.WriteString(w, "------- server stats -------\n")
	s.connection.Lock()
	fmt.Fprintf(w, "%5d named connections\n", len(s.connections))
	fmt.Fprintf(w, "%5d anonymous connections\n", len(s.anonymous))
	s.connection.Unlock()
	s.topic.Lock()
	fmt.Fprintf(w, "%5d active topics\n", len(s.topics))
	s.topic.Unlock()
	io.WriteString(w, "----------------------------\n")
}

func (s *Server) serve() error {
	defer s.w.Done()
	for {
		c, err := s.l.AcceptTCP()
		if err != nil {
			// TODO: handle "too many open files"?
			return err
		}
		go s.connect(s.configure(c))
	}
}

func (s *Server) configure(c *net.TCPConn) net.Conn {
	c.SetNoDelay(true)
	if s.cfg == nil {
		return c
	}
	return tls.Server(c, s.cfg)
}

func (s *Server) connect(c net.Conn) {
	cc, err := NewConnection(c, s.auth, s.dispatcher)
	if err != nil {
		fmt.Println("connect rejected:", err)
		if err == ErrUnauthorized {
			c.Write(s.auth.Unauthorized())
		} else if err == ErrInvalidLogin {
			c.Write(respBadRequest)
		}
		c.Close()
		return
	}
	var old *Connection
	u := cc.User
	s.connection.Lock()
	if u == ssmp.Anonymous {
		s.anonymous[cc] = cc
	} else {
		old = s.connections[u]
		s.connections[u] = cc
	}
	s.connection.Unlock()
	if old != nil {
		old.Close()
	}
}

func (s *ConnectionManager) GetConnection(user []byte) *Connection {
	s.connection.Lock()
	c := s.connections[string(user)]
	s.connection.Unlock()
	return c
}

func (s *ConnectionManager) RemoveConnection(c *Connection) {
	s.connection.Lock()
	if c.User == ssmp.Anonymous {
		delete(s.anonymous, c)
	} else if s.connections[c.User] == c {
		delete(s.connections, c.User)
	} else {
		fmt.Println("mismatching connection closed", c.User)
	}
	s.connection.Unlock()
}

func (s *TopicManager) GetOrCreateTopic(name []byte) *Topic {
	s.topic.Lock()
	t := s.topics[string(name)]
	if t == nil {
		t = NewTopic(string(name), s)
		s.topics[string(name)] = t
	}
	s.topic.Unlock()
	return t
}

func (s *TopicManager) GetTopic(name []byte) *Topic {
	s.topic.Lock()
	t := s.topics[string(name)]
	s.topic.Unlock()
	return t
}

func (s *TopicManager) RemoveTopic(name string) {
	s.topic.Lock()
	delete(s.topics, name)
	s.topic.Unlock()
}
<commit_msg>More detailed stats dump<commit_after>// Copyright (c) 2015, Air Computing Inc. <oss@aerofs.com>
// All rights reserved.

package server

import (
	"crypto/tls"
	"fmt"
	"github.com/aerofs/lipwig/ssmp"
	"io"
	"net"
	"sync"
)

// A ConnectionManager manages a set of Connection.
// All methods are safe to call from multiple goroutines simultaneously.
type ConnectionManager struct {
	connection  sync.Mutex
	anonymous   map[*Connection]*Connection
	connections map[string]*Connection
}

// A TopicManager manages a set of Topic.
// All methods are safe to call from multiple goroutines simultaneously.
type TopicManager struct {
	topic  sync.Mutex
	topics map[string]*Topic
}

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

// server implements Server, ConnectionManager and TopicManager
type Server struct {
	ConnectionManager
	TopicManager

	l    *net.TCPListener
	cfg  *tls.Config
	auth Authenticator

	// used to cleanly Stop the goroutine spawned by Start
	w sync.WaitGroup

	dispatcher *Dispatcher
}

// NewServer creates a new SSMP server from a TCP Listener, an Authenticator
// and a TLS configuration.
func NewServer(l net.Listener, auth Authenticator, cfg *tls.Config) *Server {
	s := &Server{
		l:    l.(*net.TCPListener),
		cfg:  cfg,
		auth: auth,
		ConnectionManager: ConnectionManager{
			anonymous:   make(map[*Connection]*Connection),
			connections: make(map[string]*Connection),
		},
		TopicManager: TopicManager{
			topics: make(map[string]*Topic),
		},
	}
	s.dispatcher = NewDispatcher(&s.TopicManager, &s.ConnectionManager)
	return s
}

// Serve accept connections in the calling goroutine and only returns
// in case of error.
func (s *Server) Serve() error {
	s.w.Add(1)
	return s.serve()
}

// Start accepts connection in a new goroutine and returns the Server
// This allows the following terse idiom:
//		defer s.Start().Stop()
func (s *Server) Start() *Server {
	s.w.Add(1)
	go s.serve()
	return s
}

// ListeningPort returns the TCP port to which the underlying Listener is bound.
func (s *Server) ListeningPort() int {
	return s.l.Addr().(*net.TCPAddr).Port
}

// Stop stops accepting new connections and immediately closes all existing
// connections. Serve
func (s *Server) Stop() {
	s.l.Close()
	s.connection.Lock()
	for _, c := range s.connections {
		c.Close()
	}
	for c := range s.anonymous {
		c.Close()
	}
	s.connection.Unlock()
	s.w.Wait()
}

// DumpStats writes some internal stats to the given Writer.
func (s *Server) DumpStats(w io.Writer) {
	io.WriteString(w, "------- server stats -------\n")
	s.connection.Lock()
	fmt.Fprintf(w, "%5d anonymous connections\n", len(s.anonymous))
	for c := range s.anonymous {
		fmt.Fprintf(w, "\t%p %v\n", c, c.c.RemoteAddr())
	}
	fmt.Fprintf(w, "%5d named connections\n", len(s.connections))
	for u, c := range s.connections {
		fmt.Fprintf(w, "\t%p %v %s %s\n", c, c.c.RemoteAddr(), u, c.User)
		// FIXME: synchronization to prevent race with SUB/UNSUB handling
		for n, t := range c.sub {
			fmt.Fprintf(w, "\t\t%s %p\n", n, t)
		}
	}
	s.connection.Unlock()
	s.topic.Lock()
	fmt.Fprintf(w, "%5d active topics\n", len(s.topics))
	for n, t := range s.topics {
		fmt.Fprintf(w, "\t%p %s %s\n", t, n, t.Name)
		for c, p := range t.c {
			fmt.Fprintf(w, "\t\t%p %v %s\n", c, p, c.User)
		}
	}
	s.topic.Unlock()
	io.WriteString(w, "----------------------------\n")
}

func (s *Server) serve() error {
	defer s.w.Done()
	for {
		c, err := s.l.AcceptTCP()
		if err != nil {
			// TODO: handle "too many open files"?
			return err
		}
		go s.connect(s.configure(c))
	}
}

func (s *Server) configure(c *net.TCPConn) net.Conn {
	c.SetNoDelay(true)
	if s.cfg == nil {
		return c
	}
	return tls.Server(c, s.cfg)
}

func (s *Server) connect(c net.Conn) {
	cc, err := NewConnection(c, s.auth, s.dispatcher)
	if err != nil {
		fmt.Println("connect rejected:", err)
		if err == ErrUnauthorized {
			c.Write(s.auth.Unauthorized())
		} else if err == ErrInvalidLogin {
			c.Write(respBadRequest)
		}
		c.Close()
		return
	}
	var old *Connection
	u := cc.User
	s.connection.Lock()
	if u == ssmp.Anonymous {
		s.anonymous[cc] = cc
	} else {
		old = s.connections[u]
		s.connections[u] = cc
	}
	s.connection.Unlock()
	if old != nil {
		old.Close()
	}
}

func (s *ConnectionManager) GetConnection(user []byte) *Connection {
	s.connection.Lock()
	c := s.connections[string(user)]
	s.connection.Unlock()
	return c
}

func (s *ConnectionManager) RemoveConnection(c *Connection) {
	s.connection.Lock()
	if c.User == ssmp.Anonymous {
		delete(s.anonymous, c)
	} else if s.connections[c.User] == c {
		delete(s.connections, c.User)
	} else {
		fmt.Println("mismatching connection closed", c.User)
	}
	s.connection.Unlock()
}

func (s *TopicManager) GetOrCreateTopic(name []byte) *Topic {
	s.topic.Lock()
	t := s.topics[string(name)]
	if t == nil {
		t = NewTopic(string(name), s)
		s.topics[string(name)] = t
	}
	s.topic.Unlock()
	return t
}

func (s *TopicManager) GetTopic(name []byte) *Topic {
	s.topic.Lock()
	t := s.topics[string(name)]
	s.topic.Unlock()
	return t
}

func (s *TopicManager) RemoveTopic(name string) {
	s.topic.Lock()
	delete(s.topics, name)
	s.topic.Unlock()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"

	"log"
	"net/http"
	"sync"
	"time"

	"github.com/Leimy/rx-go/bot"
	"github.com/Leimy/rx-go/nowplaying"
	"github.com/Leimy/rx-go/twit"
)

var nowPlaying *nowplaying.NowPlaying
var tweeter twit.Tweeter

var botTo struct {
	sync.RWMutex
	C chan string
}

func resetBotTo() {
	botTo.Lock()
	defer botTo.Unlock()
	botTo.C = make(chan string)
}

func getChan() (chan string) {
	botTo.RLock()
	defer botTo.RUnlock()
	return botTo.C
}

// just some counters
var stats struct {
	sync.RWMutex
	botRestarts uint64
}

func incBotRestarts() {
	stats.Lock()
	defer stats.Unlock()
	stats.botRestarts++
}

func getBotRestarts() uint64 {
	stats.RLock()
	defer stats.RUnlock()
	return stats.botRestarts
}

// Set up the metadata handler
func handleMeta() {
	http.HandleFunc("/metadata", func(w http.ResponseWriter, r *http.Request) {
		switch method := r.Method; method {
		case "GET":
			curSong := ""
			for curSong == "" {
				curSong = nowPlaying.Get()
			}
			fmt.Fprintf(w, "Now Playing: %s", curSong)
		default:
			log.Printf("%s Not implemented", method)
		}
	})
}

// Call only once per uri
func handleTwit(uri string, tweeter twit.Tweeter) {
	var message = make([]byte, 160)
	http.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) {
		switch method := r.Method; method {
		case "PUT", "POST":
			if _, err := r.Body.Read(message); err != nil && err != io.EOF {
				log.Printf("Failed to read request: %s %v", message, err)
			} else {
				log.Printf("Requested to tweet: %s", message)
				tweeter(string(message))
			}
		default:
			log.Printf("Unsupported method: %s", method)
		}
	})
}

// stats
func handleStats() {
	http.HandleFunc("/botstats", func(w http.ResponseWriter, r *http.Request) {
		switch method := r.Method; method {
		case "GET":
			fmt.Fprintf(w, "Restart count: %v", getBotRestarts())
		default:
			log.Printf("Unsupported method: %s", method)
		}
	})
}

// TODO-Maybe: module for the automatic behaviors. (only this server cares)
type autoState struct {
	sync.RWMutex
	tweet bool
	last  bool
}

func newAutoState() *autoState {
	return &autoState{sync.RWMutex{}, false, false}
}

func (as *autoState) getTweet() bool {
	as.RLock()
	defer as.RUnlock()
	return as.tweet
}

func (as *autoState) getLast() bool {
	as.RLock()
	defer as.RUnlock()
	return as.last
}

func (as *autoState) toggleTweet() {
	as.Lock()
	defer as.Unlock()
	as.tweet = !as.tweet
}

func (as *autoState) toggleLast() {
	as.Lock()
	defer as.Unlock()
	as.last = !as.last
}

var autos *autoState

// Subscribes to nowPlaying, and reads from the subscription
// Takes actions based on the settings of automatic behaviors
func metaSubscriber() {
	updates := make(chan string)
	nowPlaying.Subscribe(updates)
	for {
		for line := range updates {
			if autos.getTweet() {
				// TODO: Use the REST endpoint
				tweeter(line)
			}
			if autos.getLast() {
				getChan() <- line
			}
		}
	}
}

// Commands from IRC
func procIRCLine(line string) {
	log.Printf("got: %q", line)
	switch line {
	case "?lastsong?":
		getChan() <- nowPlaying.Get()
	case "?tweet?":
		//TODO: use the REST endpoint
		tweeter(nowPlaying.Get())
	case "?autotweet?":
		autos.toggleTweet()
		getChan() <- fmt.Sprintf("Autotweet is %v", autos.getTweet())
	case "?autolast?":
		autos.toggleLast()
		getChan() <- fmt.Sprintf("Autolast is %v", autos.getLast())
	}
}

// Keeps the bot alive, never returns
func keepBotAlive() {

	start := func(done chan struct{}, botFrom chan string) {
		defer close(done)
		resetBotTo()
		//		bot.NewBot("#radioxenu", "son_of_metabot2", "irc.radioxenu.com:6667", botFrom, getChan())
		bot.NewBot("#radioxenu", "son_of_metabot2", "localhost:6667", botFrom, getChan())
	}
	for {
		done := make(chan struct{})
		botFrom := make(chan string)
		go start(done, botFrom)
		for line := range botFrom {
			procIRCLine(line)
		}
		<-done // wait until previous bot is dead before making another
		incBotRestarts()
		time.Sleep(5 * time.Second)
	}
}

func init() {
	nowPlaying = nowplaying.NewNowPlaying("http://radioxenu.com:8000/relay")
	autos = newAutoState()
	tweeter = twit.MakeTweeter("@radioxenu http://tunein.com/radio/Radio-Xenu-s118981/")
	handleTwit("/tweet", tweeter)
	handleMeta()
	handleStats()
}

func main() {
	go nowPlaying.StartUpdating()
	go metaSubscriber()
	go keepBotAlive()
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>get rid of debugging version<commit_after>package main

import (
	"fmt"
	"io"

	"log"
	"net/http"
	"sync"
	"time"

	"github.com/Leimy/rx-go/bot"
	"github.com/Leimy/rx-go/nowplaying"
	"github.com/Leimy/rx-go/twit"
)

var nowPlaying *nowplaying.NowPlaying
var tweeter twit.Tweeter

var botTo struct {
	sync.RWMutex
	C chan string
}

func resetBotTo() {
	botTo.Lock()
	defer botTo.Unlock()
	botTo.C = make(chan string)
}

func getChan() (chan string) {
	botTo.RLock()
	defer botTo.RUnlock()
	return botTo.C
}

// just some counters
var stats struct {
	sync.RWMutex
	botRestarts uint64
}

func incBotRestarts() {
	stats.Lock()
	defer stats.Unlock()
	stats.botRestarts++
}

func getBotRestarts() uint64 {
	stats.RLock()
	defer stats.RUnlock()
	return stats.botRestarts
}

// Set up the metadata handler
func handleMeta() {
	http.HandleFunc("/metadata", func(w http.ResponseWriter, r *http.Request) {
		switch method := r.Method; method {
		case "GET":
			curSong := ""
			for curSong == "" {
				curSong = nowPlaying.Get()
			}
			fmt.Fprintf(w, "Now Playing: %s", curSong)
		default:
			log.Printf("%s Not implemented", method)
		}
	})
}

// Call only once per uri
func handleTwit(uri string, tweeter twit.Tweeter) {
	var message = make([]byte, 160)
	http.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) {
		switch method := r.Method; method {
		case "PUT", "POST":
			if _, err := r.Body.Read(message); err != nil && err != io.EOF {
				log.Printf("Failed to read request: %s %v", message, err)
			} else {
				log.Printf("Requested to tweet: %s", message)
				tweeter(string(message))
			}
		default:
			log.Printf("Unsupported method: %s", method)
		}
	})
}

// stats
func handleStats() {
	http.HandleFunc("/botstats", func(w http.ResponseWriter, r *http.Request) {
		switch method := r.Method; method {
		case "GET":
			fmt.Fprintf(w, "Restart count: %v", getBotRestarts())
		default:
			log.Printf("Unsupported method: %s", method)
		}
	})
}

// TODO-Maybe: module for the automatic behaviors. (only this server cares)
type autoState struct {
	sync.RWMutex
	tweet bool
	last  bool
}

func newAutoState() *autoState {
	return &autoState{sync.RWMutex{}, false, false}
}

func (as *autoState) getTweet() bool {
	as.RLock()
	defer as.RUnlock()
	return as.tweet
}

func (as *autoState) getLast() bool {
	as.RLock()
	defer as.RUnlock()
	return as.last
}

func (as *autoState) toggleTweet() {
	as.Lock()
	defer as.Unlock()
	as.tweet = !as.tweet
}

func (as *autoState) toggleLast() {
	as.Lock()
	defer as.Unlock()
	as.last = !as.last
}

var autos *autoState

// Subscribes to nowPlaying, and reads from the subscription
// Takes actions based on the settings of automatic behaviors
func metaSubscriber() {
	updates := make(chan string)
	nowPlaying.Subscribe(updates)
	for {
		for line := range updates {
			if autos.getTweet() {
				// TODO: Use the REST endpoint
				tweeter(line)
			}
			if autos.getLast() {
				getChan() <- line
			}
		}
	}
}

// Commands from IRC
func procIRCLine(line string) {
	log.Printf("got: %q", line)
	switch line {
	case "?lastsong?":
		getChan() <- nowPlaying.Get()
	case "?tweet?":
		//TODO: use the REST endpoint
		tweeter(nowPlaying.Get())
	case "?autotweet?":
		autos.toggleTweet()
		getChan() <- fmt.Sprintf("Autotweet is %v", autos.getTweet())
	case "?autolast?":
		autos.toggleLast()
		getChan() <- fmt.Sprintf("Autolast is %v", autos.getLast())
	}
}

// Keeps the bot alive, never returns
func keepBotAlive() {

	start := func(done chan struct{}, botFrom chan string) {
		defer close(done)
		resetBotTo()
		bot.NewBot("#radioxenu", "son_of_metabot", "irc.radioxenu.com:6667", botFrom, getChan())
	}
	for {
		done := make(chan struct{})
		botFrom := make(chan string)
		go start(done, botFrom)
		for line := range botFrom {
			procIRCLine(line)
		}
		<-done // wait until previous bot is dead before making another
		incBotRestarts()
		time.Sleep(5 * time.Second)
	}
}

func init() {
	nowPlaying = nowplaying.NewNowPlaying("http://radioxenu.com:8000/relay")
	autos = newAutoState()
	tweeter = twit.MakeTweeter("@radioxenu http://tunein.com/radio/Radio-Xenu-s118981/")
	handleTwit("/tweet", tweeter)
	handleMeta()
	handleStats()
}

func main() {
	go nowPlaying.StartUpdating()
	go metaSubscriber()
	go keepBotAlive()
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"
	"os"

	"github.com/kotakanbe/go-cve-dictionary/config"
	db "github.com/kotakanbe/go-cve-dictionary/db"
	log "github.com/kotakanbe/go-cve-dictionary/log"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	"github.com/labstack/echo/middleware"
)

// Start starts CVE dictionary HTTP Server.
func Start() error {
	e := echo.New()
	e.SetDebug(config.Conf.Debug)

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	// setup access logger
	logPath := "/var/log/vuls/access.log"
	if _, err := os.Stat(logPath); os.IsNotExist(err) {
		if _, err := os.Create(logPath); err != nil {
			return err
		}
	}
	f, err := os.OpenFile(logPath, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	logconf := middleware.DefaultLoggerConfig
	logconf.Output = f
	e.Use(middleware.LoggerFromConfig(logconf))

	// Routes
	e.Get("/health", health())
	e.Get("/cves/:id", getCve())
	e.Post("/cpes", getCveByCpeName())

	bindURL := fmt.Sprintf("%s:%s", config.Conf.Bind, config.Conf.Port)
	log.Infof("Listening on %s", bindURL)

	e.Run(standard.New(bindURL))
	return nil
}

// Handler
func health() echo.HandlerFunc {
	return func(c echo.Context) error {
		return c.String(http.StatusOK, "")
	}
}

// Handler
func getCve() echo.HandlerFunc {
	return func(c echo.Context) error {
		cveid := c.Param("id")
		cveDetail := db.Get(cveid)
		return c.JSON(http.StatusOK, cveDetail)
	}
}

type cpeName struct {
	Name string `form:"name"`
}

func getCveByCpeName() echo.HandlerFunc {
	return func(c echo.Context) error {
		cpe := cpeName{}
		err := c.Bind(&cpe)
		if err != nil {
			return err
		}
		cveDetails := db.GetByCpeName(cpe.Name)
		return c.JSON(http.StatusOK, cveDetails)
	}
}
<commit_msg>follow https://github.com/labstack/echo/commit/1a7bc67<commit_after>package server

import (
	"fmt"
	"net/http"
	"os"

	"github.com/kotakanbe/go-cve-dictionary/config"
	db "github.com/kotakanbe/go-cve-dictionary/db"
	log "github.com/kotakanbe/go-cve-dictionary/log"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	"github.com/labstack/echo/middleware"
)

// Start starts CVE dictionary HTTP Server.
func Start() error {
	e := echo.New()
	e.SetDebug(config.Conf.Debug)

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	// setup access logger
	logPath := "/var/log/vuls/access.log"
	if _, err := os.Stat(logPath); os.IsNotExist(err) {
		if _, err := os.Create(logPath); err != nil {
			return err
		}
	}
	f, err := os.OpenFile(logPath, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	logconf := middleware.DefaultLoggerConfig
	logconf.Output = f
	e.Use(middleware.LoggerWithConfig(logconf))

	// Routes
	e.Get("/health", health())
	e.Get("/cves/:id", getCve())
	e.Post("/cpes", getCveByCpeName())

	bindURL := fmt.Sprintf("%s:%s", config.Conf.Bind, config.Conf.Port)
	log.Infof("Listening on %s", bindURL)

	e.Run(standard.New(bindURL))
	return nil
}

// Handler
func health() echo.HandlerFunc {
	return func(c echo.Context) error {
		return c.String(http.StatusOK, "")
	}
}

// Handler
func getCve() echo.HandlerFunc {
	return func(c echo.Context) error {
		cveid := c.Param("id")
		cveDetail := db.Get(cveid)
		return c.JSON(http.StatusOK, cveDetail)
	}
}

type cpeName struct {
	Name string `form:"name"`
}

func getCveByCpeName() echo.HandlerFunc {
	return func(c echo.Context) error {
		cpe := cpeName{}
		err := c.Bind(&cpe)
		if err != nil {
			return err
		}
		cveDetails := db.GetByCpeName(cpe.Name)
		return c.JSON(http.StatusOK, cveDetails)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"bufio"
	"github.com/myechuri/ukd/server/api"
	"golang.org/x/net/context"
	"google.golang.org/grpc/grpclog"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"syscall"
)

type version_type struct {
	Major int32
	Minor int32
}

type ukdServer struct {
	Version    version_type
	AppProcess map[string]*os.Process
}

func (s ukdServer) GetVersion(context context.Context, request *api.VersionRequest) (*api.VersionReply, error) {
	reply := api.VersionReply{
		Major: s.Version.Major,
		Minor: s.Version.Minor}
	grpclog.Printf("Version request")
	return &reply, nil
}

func (s ukdServer) StartUK(context context.Context, request *api.StartRequest) (*api.StartReply, error) {
	grpclog.Printf("StartUK request: name: %s, Image: %s", request.Name, request.Location)

	driveArg := "file=" + request.Location + ",if=none,id=hd0,cache=none,aio=native"

	// Compose application-specific configuration.
	configRoot := "/var/lib/ukd"
	os.Mkdir(configRoot, 0777) // TODO: check error
	configRoot += "/" + request.Name
	os.Mkdir(configRoot, 0777) // TODO: check error
	qemuIfupByteArray := []byte("#!/bin/sh\n" +
		"brctl stp virbr0 off\n" +
		"brctl addif virbr0 $1\n" +
		"ifconfig $1 up\n")
	configRoot += "/qemu-ifup.sh"
	ioutil.WriteFile(configRoot, qemuIfupByteArray, 0700) // TODO: check error
	netdevArg := "tap,id=hn0,script=" + configRoot + ",vhost=on"

	cmdName := "qemu-system-x86_64"
	args := []string{
		"-m", "2G",
		"-smp", "4",
		"-vnc", ":1",
		"-gdb", "tcp::1234,server,nowait",
		"-device", "virtio-blk-pci,id=blk0,bootindex=0,drive=hd0,scsi=off",
		"-drive", driveArg,
		"-netdev", netdevArg,
		"-device", "virtio-net-pci,netdev=hn0,id=nic0",
		"-redir", "tcp:2222::22",
		"-device", "virtio-rng-pci",
		"-enable-kvm",
		"-cpu", "host,+x2apic",
		"-chardev", "stdio,mux=on,id=stdio,signal=off",
		"-mon", "chardev=stdio,mode=readline,default",
		"-device", "isa-serial,chardev=stdio"}
	cmd := exec.Command(cmdName, args...)
	stdout, _ := cmd.StdoutPipe()
	cmd.Start()

	r := bufio.NewReader(stdout)
	matched := false
	var line []byte
	for !(matched) {
		line, _, _ = r.ReadLine()
		matched, _ = regexp.MatchString("eth0:.*", string(line))
	}
	ip := strings.Fields(string(line))[1]
	s.AppProcess[request.Name] = cmd.Process

	reply := api.StartReply{
		Success: true, // TODO: gather err from previous steps
		Ip:      ip,
		Info:    "Successful start"}
	return &reply, nil
}

func (s ukdServer) StopUK(context context.Context, request *api.StopRequest) (*api.StopReply, error) {
	grpclog.Printf("StopUK request: name: %s", request.Name)
	var success bool
	var info string
	process := s.AppProcess[request.Name]
	if process == nil {
		success = true
		info = "App not found. Nothing to do."
	} else {
		process.Signal(syscall.SIGTERM)
		process.Wait()
		success = true
		info = "Successfully stopped Application"
	}
	reply := api.StopReply{
		Success: success,
		Info:    info}
	grpclog.Printf("Stop request")
	return &reply, nil

}

func NewServer() *ukdServer {
	s := &ukdServer{Version: version_type{Major: 0, Minor: 1},
		AppProcess: make(map[string]*os.Process)}
	return s
}
<commit_msg>delete deleted app<commit_after>package server

import (
	"bufio"
	"github.com/myechuri/ukd/server/api"
	"golang.org/x/net/context"
	"google.golang.org/grpc/grpclog"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"syscall"
)

type version_type struct {
	Major int32
	Minor int32
}

type ukdServer struct {
	Version    version_type
	AppProcess map[string]*os.Process
}

func (s ukdServer) GetVersion(context context.Context, request *api.VersionRequest) (*api.VersionReply, error) {
	reply := api.VersionReply{
		Major: s.Version.Major,
		Minor: s.Version.Minor}
	grpclog.Printf("Version request")
	return &reply, nil
}

func (s ukdServer) StartUK(context context.Context, request *api.StartRequest) (*api.StartReply, error) {
	grpclog.Printf("StartUK request: name: %s, Image: %s", request.Name, request.Location)

	driveArg := "file=" + request.Location + ",if=none,id=hd0,cache=none,aio=native"

	// Compose application-specific configuration.
	configRoot := "/var/lib/ukd"
	os.Mkdir(configRoot, 0777) // TODO: check error
	configRoot += "/" + request.Name
	os.Mkdir(configRoot, 0777) // TODO: check error
	qemuIfupByteArray := []byte("#!/bin/sh\n" +
		"brctl stp virbr0 off\n" +
		"brctl addif virbr0 $1\n" +
		"ifconfig $1 up\n")
	configRoot += "/qemu-ifup.sh"
	ioutil.WriteFile(configRoot, qemuIfupByteArray, 0700) // TODO: check error
	netdevArg := "tap,id=hn0,script=" + configRoot + ",vhost=on"

	cmdName := "qemu-system-x86_64"
	args := []string{
		"-m", "2G",
		"-smp", "4",
		"-vnc", ":1",
		"-gdb", "tcp::1234,server,nowait",
		"-device", "virtio-blk-pci,id=blk0,bootindex=0,drive=hd0,scsi=off",
		"-drive", driveArg,
		"-netdev", netdevArg,
		"-device", "virtio-net-pci,netdev=hn0,id=nic0",
		"-redir", "tcp:2222::22",
		"-device", "virtio-rng-pci",
		"-enable-kvm",
		"-cpu", "host,+x2apic",
		"-chardev", "stdio,mux=on,id=stdio,signal=off",
		"-mon", "chardev=stdio,mode=readline,default",
		"-device", "isa-serial,chardev=stdio"}
	cmd := exec.Command(cmdName, args...)
	stdout, _ := cmd.StdoutPipe()
	cmd.Start()

	r := bufio.NewReader(stdout)
	matched := false
	var line []byte
	for !(matched) {
		line, _, _ = r.ReadLine()
		matched, _ = regexp.MatchString("eth0:.*", string(line))
	}
	ip := strings.Fields(string(line))[1]
	s.AppProcess[request.Name] = cmd.Process

	reply := api.StartReply{
		Success: true, // TODO: gather err from previous steps
		Ip:      ip,
		Info:    "Successful start"}
	return &reply, nil
}

func (s ukdServer) StopUK(context context.Context, request *api.StopRequest) (*api.StopReply, error) {
	grpclog.Printf("StopUK request: name: %s", request.Name)
	var success bool
	var info string
	process := s.AppProcess[request.Name]
	if process == nil {
		success = true
		info = "App not found. Nothing to do."
	} else {
		process.Signal(syscall.SIGTERM) // TODO: check error
		pstate, _ := process.Wait() // TODO: check err
                if pstate.Exited() {
		    success = true
		    info = "Successfully stopped Application"
                    delete(s.AppProcess, request.Name)
                } else {
                    success = false
                    info = pstate.String()
                }
	}
	reply := api.StopReply{
		Success: success,
		Info:    info}
	grpclog.Printf("Stop request")
	return &reply, nil

}

func NewServer() *ukdServer {
	s := &ukdServer{Version: version_type{Major: 0, Minor: 1},
		AppProcess: make(map[string]*os.Process)}
	return s
}
<|endoftext|>


<commit_before><commit_msg>cache: finish implementation.<commit_after><|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"strings"
	"testing"

	"github.com/go-test/deep"
)

var checkerInvalidPrograms = []struct {
	name    string
	program string
	errors  []string
}{
	{"undefined named capture group",
		"/blurgh/ { $undef++\n }\n",
		[]string{"undefined named capture group:1:12-17: Capture group `$undef' was not defined by a regular expression visible to this scope.\n\tTry using `(?P<undef>...)' to name the capture group."}},

	{"out of bounds capref",
		"/(blyurg)/ { $2++ \n}\n",
		[]string{"out of bounds capref:1:14-15: Capture group `$2' was not defined by a regular expression " +
			"visible to this scope.\n\tCheck that there are at least 2 pairs of parentheses."},
	},

	{"undefined decorator",
		"@foo {}\n",
		[]string{"undefined decorator:1:1-4: Decorator `foo' not defined.\n\tTry adding a definition `def foo {}' earlier in the program."}},

	{"undefined identifier",
		"// { x++ \n}\n",
		[]string{"undefined identifier:1:6: Identifier `x' not declared.\n\tTry adding `counter x' to the top of the program."},
	},

	{"invalid regex",
		"/foo(/ {}\n",
		[]string{"invalid regex:1:1-6: error parsing regexp: missing closing ): `foo(`"}},

	{"invalid regex 2",
		"/blurg(?P<x.)/ {}\n",
		[]string{"invalid regex 2:1:1-14: error parsing regexp: invalid named capture: `(?P<x.)`"}},

	{"invalid regex 3",
		"/blurg(?P<x>[[:alph:]])/ {}\n",
		[]string{"invalid regex 3:1:1-24: error parsing regexp: invalid character class range: `[:alph:]`"}},

	{"duplicate declaration",
		"counter foo\ncounter foo\n",
		[]string{"duplicate declaration:2:9-11: Redeclaration of metric `foo' previously declared at duplicate declaration:1:9-11"}},

	// 	{"indexedExpr parameter count",
	// 		`counter foo by a, b
	// counter bar by a, b
	// counter quux by a
	// /(\d+)/ {
	//   foo[$1]++
	//   bar[$1][0]++
	//   quux[$1][0]++
	// }
	// 	`,
	// 		[]string{"indexedExpr parameter count:5:3-8: index lookup: type mismatch: \"→ typeVar1 typeVar2 typeVar3\" != \"→ Int typeVar9\"",
	// 			"indexedExpr parameter count:7:3-12: Too many keys for metric"}},

	{"builtin parameter mismatch",
		`/(\d+)/ {
	  strptime()
	}
    /(\d+)/ {
	  timestamp()
	}
	`,
		[]string{"builtin parameter mismatch:2:13: call to `strptime': type mismatch; expected \"→ String None\" received \"→ typeVar5\""}},
}

func TestCheckInvalidPrograms(t *testing.T) {
	for _, tc := range checkerInvalidPrograms {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			ast, err := Parse(tc.name, strings.NewReader(tc.program))
			if err != nil {
				t.Fatal(err)
			}
			err = Check(ast)
			if err == nil {
				t.Fatal("check didn't fail")
			}

			diff := deep.Equal(
				strings.Join(tc.errors, "\n"),        // want
				strings.TrimRight(err.Error(), "\n")) // got
			if diff != nil {
				t.Error(diff)
				s := Sexp{}
				s.emitTypes = true
				t.Log(s.Dump(ast))
			}
		})
	}
}

var checkerValidPrograms = []struct {
	name    string
	program string
}{
	{"capture group",
		`counter foo
/(.*)/ {
  foo += $1
}
`,
	},
	{"shadowed positionals",
		`counter foo
/(.*)/ {
  foo += $1
  /bar(\d+)/ {
   foo += $1
  }
}
`},
	{"sibling positionals",
		`counter foo
/(.*)/ {
  foo += $1
}
/bar(\d+)/ {
   foo += $1
}
`},

	{"index expression",
		`counter foo by a, b
/(\d)/ {
  foo[1,$1] = 3
}`},

	// TODO(jaq): Issue 73, 68
	// {"odd indexes",
	// 	`counter foo by a,b,c
	// /(\d) (\d)/ {
	//   foo[$1,$2][0]++
	// }
	// `},
}

func TestCheckValidPrograms(t *testing.T) {
	for _, tc := range checkerValidPrograms {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			ast, err := Parse(tc.name, strings.NewReader(tc.program))
			s := Sexp{}
			s.emitTypes = true
			t.Log(s.Dump(ast))
			if err != nil {
				s := Sexp{}
				s.emitTypes = true
				t.Fatal(s.Dump(ast))
			}
			err = Check(ast)
			if err != nil {
				t.Errorf("check failed: %s", err)
			}
		})
	}
}

var checkerTypeExpressionTests = []struct {
	name     string
	expr     astNode
	expected Type
}{
	{"Int + Int -> Int",
		&binaryExprNode{lhs: &intConstNode{position{}, 1},
			rhs: &intConstNode{position{}, 1},
			op:  PLUS},
		Int,
	},
	// TODO(jaq): subtypes
	// {"Int + Float -> Float",
	// 	&binaryExprNode{lhs: &intConstNode{position{}, 1},
	// 		rhs: &floatConstNode{position{}, 1.0},
	// 		op:  PLUS},
	// 	Float,
	// },
	{"⍺ + Float -> Float",
		&binaryExprNode{lhs: &idNode{pos: position{}, sym: &Symbol{Name: "i", Kind: VarSymbol, Type: NewTypeVariable()}},
			rhs: &caprefNode{pos: position{}, sym: &Symbol{Kind: CaprefSymbol, Type: Float}},
			op:  PLUS},
		Float,
	},
}

func TestCheckTypeExpressions(t *testing.T) {
	defaultCompareUnexportedFields := deep.CompareUnexportedFields
	deep.CompareUnexportedFields = true
	defer func() { deep.CompareUnexportedFields = defaultCompareUnexportedFields }()

	for _, tc := range checkerTypeExpressionTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			err := Check(tc.expr)
			if err != nil {
				t.Fatalf("check error: %s", err)
			}

			diff := deep.Equal(tc.expected, tc.expr.Type().Root())
			if len(diff) > 0 {
				t.Error(diff)
				s := Sexp{}
				s.emitTypes = true
				t.Log("Typed AST:\n" + s.Dump(tc.expr))
			}
		})
	}
}
<commit_msg>Improves debugging of test.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"strings"
	"testing"

	"github.com/go-test/deep"
)

var checkerInvalidPrograms = []struct {
	name    string
	program string
	errors  []string
}{
	{"undefined named capture group",
		"/blurgh/ { $undef++\n }\n",
		[]string{"undefined named capture group:1:12-17: Capture group `$undef' was not defined by a regular expression visible to this scope.\n\tTry using `(?P<undef>...)' to name the capture group."}},

	{"out of bounds capref",
		"/(blyurg)/ { $2++ \n}\n",
		[]string{"out of bounds capref:1:14-15: Capture group `$2' was not defined by a regular expression " +
			"visible to this scope.\n\tCheck that there are at least 2 pairs of parentheses."},
	},

	{"undefined decorator",
		"@foo {}\n",
		[]string{"undefined decorator:1:1-4: Decorator `foo' not defined.\n\tTry adding a definition `def foo {}' earlier in the program."}},

	{"undefined identifier",
		"// { x++ \n}\n",
		[]string{"undefined identifier:1:6: Identifier `x' not declared.\n\tTry adding `counter x' to the top of the program."},
	},

	{"invalid regex",
		"/foo(/ {}\n",
		[]string{"invalid regex:1:1-6: error parsing regexp: missing closing ): `foo(`"}},

	{"invalid regex 2",
		"/blurg(?P<x.)/ {}\n",
		[]string{"invalid regex 2:1:1-14: error parsing regexp: invalid named capture: `(?P<x.)`"}},

	{"invalid regex 3",
		"/blurg(?P<x>[[:alph:]])/ {}\n",
		[]string{"invalid regex 3:1:1-24: error parsing regexp: invalid character class range: `[:alph:]`"}},

	{"duplicate declaration",
		"counter foo\ncounter foo\n",
		[]string{"duplicate declaration:2:9-11: Redeclaration of metric `foo' previously declared at duplicate declaration:1:9-11"}},

	// 	{"indexedExpr parameter count",
	// 		`counter foo by a, b
	// counter bar by a, b
	// counter quux by a
	// /(\d+)/ {
	//   foo[$1]++
	//   bar[$1][0]++
	//   quux[$1][0]++
	// }
	// 	`,
	// 		[]string{"indexedExpr parameter count:5:3-8: index lookup: type mismatch: \"→ typeVar1 typeVar2 typeVar3\" != \"→ Int typeVar9\"",
	// 			"indexedExpr parameter count:7:3-12: Too many keys for metric"}},

	{"builtin parameter mismatch",
		`/(\d+)/ {
	  strptime()
	}
    /(\d+)/ {
	  timestamp()
	}
	`,
		[]string{"builtin parameter mismatch:2:13: call to `strptime': type mismatch; expected \"→ String None\" received \"→ typeVar5\""}},
}

func TestCheckInvalidPrograms(t *testing.T) {
	for _, tc := range checkerInvalidPrograms {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			ast, err := Parse(tc.name, strings.NewReader(tc.program))
			if err != nil {
				t.Fatal(err)
			}
			err = Check(ast)
			if err == nil {
				t.Fatal("check didn't fail")
			}

			diff := deep.Equal(
				strings.Join(tc.errors, "\n"),        // want
				strings.TrimRight(err.Error(), "\n")) // got
			if diff != nil {
				t.Error(diff)
				s := Sexp{}
				s.emitTypes = true
				t.Log(s.Dump(ast))
			}
		})
	}
}

var checkerValidPrograms = []struct {
	name    string
	program string
}{
	{"capture group",
		`counter foo
/(.*)/ {
  foo += $1
}
`,
	},
	{"shadowed positionals",
		`counter foo
/(.*)/ {
  foo += $1
  /bar(\d+)/ {
   foo += $1
  }
}
`},
	{"sibling positionals",
		`counter foo
/(.*)/ {
  foo += $1
}
/bar(\d+)/ {
   foo += $1
}
`},

	{"index expression",
		`counter foo by a, b
/(\d)/ {
  foo[1,$1] = 3
}`},

	// TODO(jaq): Issue 73, 68
	// {"odd indexes",
	// 	`counter foo by a,b,c
	// /(\d) (\d)/ {
	//   foo[$1,$2][0]++
	// }
	// `},
}

func TestCheckValidPrograms(t *testing.T) {
	for _, tc := range checkerValidPrograms {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			ast, err := Parse(tc.name, strings.NewReader(tc.program))
			s := Sexp{}
			s.emitTypes = true
			t.Log("Typed AST:\n" + s.Dump(ast))
			if err != nil {
				t.Fatal(err)
			}
			err = Check(ast)
			if err != nil {
				t.Errorf("check failed: %s", err)
			}
		})
	}
}

var checkerTypeExpressionTests = []struct {
	name     string
	expr     astNode
	expected Type
}{
	{"Int + Int -> Int",
		&binaryExprNode{lhs: &intConstNode{position{}, 1},
			rhs: &intConstNode{position{}, 1},
			op:  PLUS},
		Int,
	},
	// TODO(jaq): subtypes
	// {"Int + Float -> Float",
	// 	&binaryExprNode{lhs: &intConstNode{position{}, 1},
	// 		rhs: &floatConstNode{position{}, 1.0},
	// 		op:  PLUS},
	// 	Float,
	// },
	{"⍺ + Float -> Float",
		&binaryExprNode{lhs: &idNode{pos: position{}, sym: &Symbol{Name: "i", Kind: VarSymbol, Type: NewTypeVariable()}},
			rhs: &caprefNode{pos: position{}, sym: &Symbol{Kind: CaprefSymbol, Type: Float}},
			op:  PLUS},
		Float,
	},
}

func TestCheckTypeExpressions(t *testing.T) {
	defaultCompareUnexportedFields := deep.CompareUnexportedFields
	deep.CompareUnexportedFields = true
	defer func() { deep.CompareUnexportedFields = defaultCompareUnexportedFields }()

	for _, tc := range checkerTypeExpressionTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			err := Check(tc.expr)
			if err != nil {
				t.Fatalf("check error: %s", err)
			}

			diff := deep.Equal(tc.expected, tc.expr.Type().Root())
			if len(diff) > 0 {
				t.Error(diff)
				s := Sexp{}
				s.emitTypes = true
				t.Log("Typed AST:\n" + s.Dump(tc.expr))
			}
		})
	}
}
<|endoftext|>


<commit_before>package analyze

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"regexp"

	"strings"

	"github.com/deckarep/golang-set"
	"github.com/jzelinskie/geddit"
)

func AnalyzeComments(s *geddit.OAuthSession, comments []*geddit.Comment) error {
	r, err := regexp.Compile(`github\.com\/[\w\-]+\/[\w\-]+`)
	if err != nil {
		return err
	}
	for _, comment := range comments {
		if (comment.Author == "github-stats-bot") || (strings.Contains(strings.ToLower(comment.Author), "bot")) {
			continue
		}
		if r.MatchString(comment.Body) {
			linkSet := mapset.NewSet()
			for _, link := range r.FindAllString(comment.Body, -1) {
				linkSet.Add(link)
			}
			var links []string
			for _, link := range linkSet.ToSlice() {
				links = append(links, link.(string))
			}
			if err = postReply(s, comment, links); err != nil {
				return err
			}
		}
	}
	return nil
}

func postReply(s *geddit.OAuthSession, comment *geddit.Comment, links []string) error {
	footer := "***\n^(This is Earth radio, and now here's human music ♫)\n\n^[Source](https://github.com/anaskhan96/github-stats-bot) ^| ^[PMme](https://np.reddit.com/message/compose?to=github-stats-bot)"
	var reply string
	for _, link := range links {
		var data map[string]interface{}
		if err := getStats(link, &data); err != nil {
			return err
		}
		if data["message"] == "Not Found" {
			return errors.New("Wrong GitHub API endpoint")
		}
		description := data["description"].(string)
		// The things I have to do to check if it's a bot
		if strings.Contains(strings.ToLower(description), "bot") {
			continue
		}
		stargazers := int(data["stargazers_count"].(float64))
		forks := int(data["forks_count"].(float64))
		issuesURL := "https://" + link + "/issues"
		pullsURL := "https://" + link + "/pulls"
		reply += fmt.Sprintf("\n[%s](https://%s)\n\n> *Description*: %s\n\n> *Stars*: %d\n\n> *Forks*: %d\n\n> [Issues](%s) | [Pull Requests](%s)\n\n",
			link[11:], link, description, stargazers, forks, issuesURL, pullsURL)
	}
	reply += footer
	if _, err := s.Reply(comment, reply); err != nil {
		return err
	}
	log.Println("Replied to", comment.Author)
	return nil
}

func getStats(link string, data *map[string]interface{}) error {
	res, err := http.Get("https://api.github.com/repos/" + link[11:])
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if err = json.NewDecoder(res.Body).Decode(&data); err != nil {
		return err
	}
	return nil
}
<commit_msg>minor fix<commit_after>package analyze

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"regexp"

	"strings"

	"github.com/deckarep/golang-set"
	"github.com/jzelinskie/geddit"
)

func AnalyzeComments(s *geddit.OAuthSession, comments []*geddit.Comment) error {
	r, err := regexp.Compile(`github\.com\/[\w\-]+\/[\w\-]+`)
	if err != nil {
		return err
	}
	for _, comment := range comments {
		if (comment.Author == "github-stats-bot") || (strings.Contains(strings.ToLower(comment.Author), "bot")) {
			continue
		}
		if r.MatchString(comment.Body) {
			linkSet := mapset.NewSet()
			for _, link := range r.FindAllString(comment.Body, -1) {
				linkSet.Add(link)
			}
			var links []string
			for _, link := range linkSet.ToSlice() {
				links = append(links, link.(string))
			}
			if err = postReply(s, comment, links); err != nil {
				return err
			}
		}
	}
	return nil
}

func postReply(s *geddit.OAuthSession, comment *geddit.Comment, links []string) error {
	footer := "***\n^(This is Earth radio, and now here's human music ♫)\n\n^[Source](https://github.com/anaskhan96/github-stats-bot) ^| ^[PMme](https://np.reddit.com/message/compose?to=github-stats-bot)"
	var reply string
	for _, link := range links {
		var data map[string]interface{}
		if err := getStats(link, &data); err != nil {
			return err
		}
		if data["message"] == "Not Found" {
			return errors.New("Wrong GitHub API endpoint")
		}
		description := data["description"].(string)
		// The things I have to do to check if it's a bot
		if strings.Contains(strings.ToLower(description), "bot") {
			return nil
		}
		stargazers := int(data["stargazers_count"].(float64))
		forks := int(data["forks_count"].(float64))
		issuesURL := "https://" + link + "/issues"
		pullsURL := "https://" + link + "/pulls"
		reply += fmt.Sprintf("\n[%s](https://%s)\n\n> *Description*: %s\n\n> *Stars*: %d\n\n> *Forks*: %d\n\n> [Issues](%s) | [Pull Requests](%s)\n\n",
			link[11:], link, description, stargazers, forks, issuesURL, pullsURL)
	}
	reply += footer
	if _, err := s.Reply(comment, reply); err != nil {
		return err
	}
	log.Println("Replied to", comment.Author)
	return nil
}

func getStats(link string, data *map[string]interface{}) error {
	res, err := http.Get("https://api.github.com/repos/" + link[11:])
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if err = json.NewDecoder(res.Body).Decode(&data); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package service

import (
	"encoding/base64"
	"fmt"
	"strings"

	"github.com/Rugvip/bullettime/interfaces"
	"github.com/Rugvip/bullettime/types"
	"github.com/Rugvip/bullettime/utils"
)

func CreateTokenService() (interfaces.TokenService, error) {
	return tokenService{}, nil
}

type tokenService struct{}

type tokenInfo struct {
	userId types.UserId
}

func (t tokenInfo) String() string {
	encodedUserId := base64.URLEncoding.EncodeToString([]byte(t.userId.String()))
	encodedUserId = strings.TrimRight(encodedUserId, "=")
	return fmt.Sprintf("%s..%s", encodedUserId, utils.RandomString(16))
}

func (t tokenInfo) UserId() types.UserId {
	return t.userId
}

func (t tokenService) NewAccessToken(userId types.UserId) (interfaces.Token, types.Error) {
	return tokenInfo{userId}, nil
}

func (t tokenService) ParseAccessToken(token string) (interfaces.Token, types.Error) {
	splits := strings.Split(token, "..")
	if len(splits) != 2 {
		return nil, types.DefaultUnknownTokenError
	}
	userIdStr, err := base64.URLEncoding.DecodeString(splits[0])
	if err != nil {
		return nil, types.DefaultUnknownTokenError
	}
	userId, err := types.ParseUserId(string(userIdStr))
	if err != nil {
		return nil, types.DefaultUnknownTokenError
	}
	return tokenInfo{userId}, nil
}
<commit_msg>use base64.RawURLEncoding for tokens<commit_after>package service

import (
	"encoding/base64"
	"fmt"
	"strings"

	"github.com/Rugvip/bullettime/interfaces"
	"github.com/Rugvip/bullettime/types"
	"github.com/Rugvip/bullettime/utils"
)

func CreateTokenService() (interfaces.TokenService, error) {
	return tokenService{}, nil
}

type tokenService struct{}

type tokenInfo struct {
	userId types.UserId
}

func (t tokenInfo) String() string {
	encodedUserId := base64.RawURLEncoding.EncodeToString([]byte(t.userId.String()))
	return fmt.Sprintf("%s..%s", encodedUserId, utils.RandomString(16))
}

func (t tokenInfo) UserId() types.UserId {
	return t.userId
}

func (t tokenService) NewAccessToken(userId types.UserId) (interfaces.Token, types.Error) {
	return tokenInfo{userId}, nil
}

func (t tokenService) ParseAccessToken(token string) (interfaces.Token, types.Error) {
	splits := strings.Split(token, "..")
	if len(splits) != 2 {
		return nil, types.DefaultUnknownTokenError
	}
	userIdStr, err := base64.RawURLEncoding.DecodeString(splits[0])
	if err != nil {
		return nil, types.DefaultUnknownTokenError
	}
	userId, err := types.ParseUserId(string(userIdStr))
	if err != nil {
		return nil, types.DefaultUnknownTokenError
	}
	return tokenInfo{userId}, nil
}
<|endoftext|>


<commit_before>// Copyright 2018 ETH Zurich
//
// 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 pkicmn contains some commonly used functionality and definitions.
package pkicmn

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
)

const (
	CertNameFmt        = "ISD%d-AS%s-V%d.crt"
	CoreCertNameFmt    = "ISD%d-AS%s-V%d-core.crt"
	TrcNameFmt         = "ISD%d-V%d.trc"
	ErrInvalidSelector = "Invalid selector."
)

var (
	RootDir string
	Force   bool
)

// ProcessSelector processes the given selector and returns a mapping from ISD id to ASes
// of that ISD. In case of an ISD-only selector, i.e., a '*' or any number the lists of
// ASes will be empty.
func ProcessSelector(selector string) (map[addr.ISD][]addr.IA, error) {
	toks := strings.Split(selector, "-")
	if len(toks) > 2 {
		return nil, common.NewBasicError(ErrInvalidSelector, nil, "selector", selector)
	}
	isdTok := toks[0]
	asTok := "*"
	if len(toks) == 2 {
		asTok = toks[1]
	}
	// Validate selectors.
	if isdTok == "*" && asTok != "*" {
		return nil, common.NewBasicError(ErrInvalidSelector, nil, "selector", selector)
	}
	if isdTok != "*" {
		if _, err := addr.ISDFromString(isdTok); err != nil {
			return nil, common.NewBasicError(ErrInvalidSelector, err, "selector", selector)
		}
	}
	if asTok != "*" {
		if _, err := addr.ASFromString(asTok); err != nil {
			return nil, common.NewBasicError(ErrInvalidSelector, err, "selector", selector)
		}
	}
	isdDirs, err := filepath.Glob(filepath.Join(RootDir, fmt.Sprintf("ISD%s", isdTok)))
	if err != nil {
		return nil, err
	}
	if len(isdDirs) == 0 {
		return nil, common.NewBasicError("No directories found", nil, "selector", selector)
	}
	res := make(map[addr.ISD][]addr.IA)
	for _, dir := range isdDirs {
		isd, err := isdFromDir(dir)
		if err != nil {
			return nil, err
		}
		dirs, err := filepath.Glob(filepath.Join(dir, fmt.Sprintf("AS%s", asTok)))
		if err != nil {
			return nil, err
		}
		if len(dirs) == 0 {
			return nil, common.NewBasicError("No directories found", nil, "selector", selector)
		}
		ases := make([]addr.IA, len(dirs))
		for i, asDir := range dirs {
			as, err := asFromDir(asDir)
			if err != nil {
				return nil, err
			}
			ases[i] = addr.IA{I: addr.ISD(isd), A: addr.AS(as)}
		}
		res[isd] = ases
	}
	return res, nil
}

func isdFromDir(dir string) (addr.ISD, error) {
	isd, err := addr.ISDFromString(filepath.Base(dir)[3:])
	if err != nil {
		return 0, common.NewBasicError("Unable to parse ISD number from dir", err, "dir", dir)
	}
	return isd, nil
}

func asFromDir(dir string) (addr.AS, error) {
	as, err := addr.ASFromString(filepath.Base(dir)[2:])
	if err != nil {
		return 0, common.NewBasicError("Unable to parse AS number from dir", err, "dir", dir)
	}
	return as, nil
}

func Contains(ases []addr.IA, as addr.IA) bool {
	for _, ia := range ases {
		if ia.Eq(as) {
			return true
		}
	}
	return false
}

func WriteToFile(raw common.RawBytes, path string, perm os.FileMode) error {
	// Check if file already exists.
	if _, err := os.Stat(path); err == nil {
		if !Force {
			fmt.Printf("%s already exists. Use -f to overwrite.\n", path)
			return nil
		}
		// Nuke file to ensure correct permissions.
		if err = os.Remove(path); err != nil {
			return err
		}
	}
	if err := ioutil.WriteFile(path, append(raw, "\n"...), perm); err != nil {
		return err
	}
	fmt.Println("Successfully written", path)
	return nil
}

func GetAsPath(ia addr.IA) string {
	return filepath.Join(RootDir, fmt.Sprintf("ISD%d/AS%s", ia.I, ia.A))
}

func GetIsdPath(isd addr.ISD) string {
	return filepath.Join(RootDir, fmt.Sprintf("ISD%d", isd))
}
<commit_msg>Improve tmpl error message (#1469)<commit_after>// Copyright 2018 ETH Zurich
//
// 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 pkicmn contains some commonly used functionality and definitions.
package pkicmn

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
)

const (
	CertNameFmt        = "ISD%d-AS%s-V%d.crt"
	CoreCertNameFmt    = "ISD%d-AS%s-V%d-core.crt"
	TrcNameFmt         = "ISD%d-V%d.trc"
	ErrInvalidSelector = "Invalid selector."
)

var (
	RootDir string
	Force   bool
)

// ProcessSelector processes the given selector and returns a mapping from ISD id to ASes
// of that ISD. In case of an ISD-only selector, i.e., a '*' or any number the lists of
// ASes will be empty.
func ProcessSelector(selector string) (map[addr.ISD][]addr.IA, error) {
	toks := strings.Split(selector, "-")
	if len(toks) > 2 {
		return nil, common.NewBasicError(ErrInvalidSelector, nil, "selector", selector)
	}
	isdTok := toks[0]
	asTok := "*"
	if len(toks) == 2 {
		asTok = toks[1]
	}
	// Validate selectors.
	if isdTok == "*" && asTok != "*" {
		return nil, common.NewBasicError(ErrInvalidSelector, nil, "selector", selector)
	}
	if isdTok != "*" {
		if _, err := addr.ISDFromString(isdTok); err != nil {
			return nil, common.NewBasicError(ErrInvalidSelector, err, "selector", selector)
		}
	}
	if asTok != "*" {
		if _, err := addr.ASFromString(asTok); err != nil {
			return nil, common.NewBasicError(ErrInvalidSelector, err, "selector", selector)
		}
	}
	isdDirs, err := filepath.Glob(filepath.Join(RootDir, fmt.Sprintf("ISD%s", isdTok)))
	if err != nil {
		return nil, err
	}
	if len(isdDirs) == 0 {
		return nil, common.NewBasicError("No ISD directories found", nil, "selector", selector)
	}
	res := make(map[addr.ISD][]addr.IA)
	for _, dir := range isdDirs {
		isd, err := isdFromDir(dir)
		if err != nil {
			return nil, err
		}
		dirs, err := filepath.Glob(filepath.Join(dir, fmt.Sprintf("AS%s", asTok)))
		if err != nil {
			return nil, err
		}
		if len(dirs) == 0 {
			return nil, common.NewBasicError("No AS directories found", nil, "selector", selector)
		}
		ases := make([]addr.IA, len(dirs))
		for i, asDir := range dirs {
			as, err := asFromDir(asDir)
			if err != nil {
				return nil, err
			}
			ases[i] = addr.IA{I: addr.ISD(isd), A: addr.AS(as)}
		}
		res[isd] = ases
	}
	return res, nil
}

func isdFromDir(dir string) (addr.ISD, error) {
	isd, err := addr.ISDFromString(filepath.Base(dir)[3:])
	if err != nil {
		return 0, common.NewBasicError("Unable to parse ISD number from dir", err, "dir", dir)
	}
	return isd, nil
}

func asFromDir(dir string) (addr.AS, error) {
	as, err := addr.ASFromString(filepath.Base(dir)[2:])
	if err != nil {
		return 0, common.NewBasicError("Unable to parse AS number from dir", err, "dir", dir)
	}
	return as, nil
}

func Contains(ases []addr.IA, as addr.IA) bool {
	for _, ia := range ases {
		if ia.Eq(as) {
			return true
		}
	}
	return false
}

func WriteToFile(raw common.RawBytes, path string, perm os.FileMode) error {
	// Check if file already exists.
	if _, err := os.Stat(path); err == nil {
		if !Force {
			fmt.Printf("%s already exists. Use -f to overwrite.\n", path)
			return nil
		}
		// Nuke file to ensure correct permissions.
		if err = os.Remove(path); err != nil {
			return err
		}
	}
	if err := ioutil.WriteFile(path, append(raw, "\n"...), perm); err != nil {
		return err
	}
	fmt.Println("Successfully written", path)
	return nil
}

func GetAsPath(ia addr.IA) string {
	return filepath.Join(RootDir, fmt.Sprintf("ISD%d/AS%s", ia.I, ia.A))
}

func GetIsdPath(isd addr.ISD) string {
	return filepath.Join(RootDir, fmt.Sprintf("ISD%d", isd))
}
<|endoftext|>


<commit_before>package crocker

import (
	"fmt"
	. "polydawn.net/gosh/psh"
	"time"
)

/**
 * @param dir relative path to dock dir.
 */
func NewDock(dir string) *Dock {
	dock := &Dock{dir: dir}
	//TODO ping if exists, spawn process if not, etc.
	Sh("mkdir")("-p")(DefaultIO)(dock.Dir())()
	dock.daemon().Start()
	dock.isMine = true
	time.Sleep(200 * time.Millisecond)
	return dock
}

type Dock struct {
	/**
	 * Absolute path to the base dir for a docker daemon.
	 *
	 * 'docker.sock' and 'docker.pid' are expected to exist immediately inside this path.
	 * The daemon's working dir may also be here.
	 *
	 * The last segment of the path is quite probably a symlink, and should be respected
	 * even if dangling (unless that means making more than one directory on the far
	 * side; if things are that dangling, give up).
	 */
	dir string

	/**
	 * True iff the daemon at this dock location was spawned by us.
	 * Basically used to determine if Slay() should actually fire teh lazors or not.
	 */
	isMine bool
}

func (dock Dock) Dir() string {
	return dock.dir
}

func (dock *Dock) cmd() Shfn {
	return Sh("docker")(DefaultIO)(
		"-H="+fmt.Sprintf("unix://%s", dock.GetSockPath()),
	)
}

func (dock *Dock) Client() Shfn {
	return dock.cmd()
}

func (dock *Dock) GetPidfilePath() string {
	return fmt.Sprintf("/%s/%s", dock.Dir(), "docker.pid")
}

func (dock *Dock) GetSockPath() string {
	return fmt.Sprintf("/%s/%s", dock.Dir(), "docker.sock")
}

func (dock *Dock) daemon() Shfn {
	return dock.cmd()(
		"-d",
		"-g="+dock.Dir(),
		"-p="+dock.GetPidfilePath(),
	)(Opts{Cwd: dock.Dir()})
}

func (dock *Dock) Slay() {
	if !dock.isMine { return; }
	Sh("bash")("-c")(DefaultIO)("kill `cat \""+dock.GetPidfilePath()+"\"`")()
}
<commit_msg>fix typo that forced Dock sock and pidfile to be absolute paths, contrary to documentation.<commit_after>package crocker

import (
	"fmt"
	. "polydawn.net/gosh/psh"
	"time"
)

/**
 * @param dir relative path to dock dir.
 */
func NewDock(dir string) *Dock {
	dock := &Dock{dir: dir}
	//TODO ping if exists, spawn process if not, etc.
	Sh("mkdir")("-p")(DefaultIO)(dock.Dir())()
	dock.daemon().Start()
	dock.isMine = true
	time.Sleep(200 * time.Millisecond)
	return dock
}

type Dock struct {
	/**
	 * Absolute path to the base dir for a docker daemon.
	 *
	 * 'docker.sock' and 'docker.pid' are expected to exist immediately inside this path.
	 * The daemon's working dir may also be here.
	 *
	 * The last segment of the path is quite probably a symlink, and should be respected
	 * even if dangling (unless that means making more than one directory on the far
	 * side; if things are that dangling, give up).
	 */
	dir string

	/**
	 * True iff the daemon at this dock location was spawned by us.
	 * Basically used to determine if Slay() should actually fire teh lazors or not.
	 */
	isMine bool
}

func (dock Dock) Dir() string {
	return dock.dir
}

func (dock *Dock) cmd() Shfn {
	return Sh("docker")(DefaultIO)(
		"-H="+fmt.Sprintf("unix://%s", dock.GetSockPath()),
	)
}

func (dock *Dock) Client() Shfn {
	return dock.cmd()
}

func (dock *Dock) GetPidfilePath() string {
	return fmt.Sprintf("%s/%s", dock.Dir(), "docker.pid")
}

func (dock *Dock) GetSockPath() string {
	return fmt.Sprintf("%s/%s", dock.Dir(), "docker.sock")
}

func (dock *Dock) daemon() Shfn {
	return dock.cmd()(
		"-d",
		"-g="+dock.Dir(),
		"-p="+dock.GetPidfilePath(),
	)(Opts{Cwd: dock.Dir()})
}

func (dock *Dock) Slay() {
	if !dock.isMine { return; }
	Sh("bash")("-c")(DefaultIO)("kill `cat \""+dock.GetPidfilePath()+"\"`")()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"flag"
	"fmt"
	"log"
	"math"
	"math/big"
	"strconv"
)

var (
	startingCards = flag.Int("starting_cards", 5, "Number of cards each player should start with.")
	numGames      = flag.Int("num_games", 1000, "Number of games that will be played.")
	numTests      = flag.Int("num_tests", 100, "Number of tests to be performed.")
	playerAI      = flag.String("player_ai", "randomAI", "AI to be used by the main player.")
	opponentAI    = flag.String("opponent_ai", "onlyFirstAI", "AI to be used by the opponent player.")
	randomStart   = flag.Bool("random_start", true, "Defines who starts randomly. If false, the first player always starts.")
	debug         = flag.Bool("debug", false, "Print debug information")
)

func main() {
	flag.Parse()
	var results []int
	total := 0
	for i := 0; i < *numTests; i++ {
		res := runGame()
		results = append(results, res)
		total += res
	}
	avg := float64(total) / float64(*numTests)
	variance := 0.0
	for _, res := range results {
		term := float64(res) - avg
		variance += term * term
	}
	variance /= float64(*numTests)
	fmt.Printf("%v+-%v\n", avg, math.Sqrt(variance))
}

func runGame() int {
	w := 0
	for i := 0; i < *numGames; i++ {
		g := newGame()
		if g.play() == 0 {
			w++
		}
	}
	return w
}

type game struct {
	players []*player
	playing int
	deck    *deck
	top     *card
	asked   suit
	order   int
	garbage []*card
}

func newGame() *game {
	g := &game{}
	if *randomStart {
		if *debug {
			fmt.Println("random_start")
		}
		g.playing = randInt(2)
	}
	g.deck = newDeck()
	g.players = []*player{newPlayer(*playerAI), newPlayer(*opponentAI)}
	for nc := 0; nc < *startingCards; nc++ {
		g.players[0].addCard(g.getCard())
		g.players[1].addCard(g.getCard())
	}
	g.top = g.getCard()
	g.order = 1
	return g
}

func (g *game) play() int {
	for {
		top, asked := g.player().play(g.top, g.asked, g.deck)
		if *debug {
			fmt.Println(g, top, asked)
		}
		if top == nil {
			g.addCard()
			g.next()
			continue
		}
		isPlayValid(top, asked, g.top, g.asked)
		if g.end() {
			return g.playing
		}
		g.garbage = append(g.garbage, g.top)
		g.top, g.asked = top, asked
		switch g.top.n {
		case 1:
		case 12:
			if len(g.players) == 2 {
				g.next()
			} else {
				g.order *= -1
			}
		case 7:
			g.next()
			g.addCard()
			g.addCard()
		}
		g.next()
	}
}

func (g *game) player() *player {
	return g.players[g.playing]
}

func (g *game) next() {
	g.playing = (g.playing + g.order + len(g.players)) % len(g.players)
}

func (g *game) getCard() *card {
	c := g.deck.getCard()
	if c != nil {
		return c
	}
	if g.garbage == nil {
		log.Fatal("empty garbage")
	}
	g.deck.cs = g.garbage
	g.deck.shuffle()
	g.garbage = nil
	return g.deck.getCard()
}

func (g *game) addCard() {
	g.player().addCard(g.getCard())
}

func (g *game) end() bool {
	return g.player().end()
}

func (g *game) String() string {
	var b bytes.Buffer
	b.WriteString(fmt.Sprintf("-> %v %v %v %v %v %v ", len(g.deck.cs), len(g.garbage), g.playing, g.top, g.asked, g.order))
	for _, p := range g.players {
		b.WriteString(p.String())
		b.WriteString(" ")
	}
	return b.String()
}

type deck struct {
	cs []*card
}

func newDeck() *deck {
	d := &deck{}
	for n := 1; n <= 13; n++ {
		for s := spades; s <= clubs; s++ {
			d.cs = append(d.cs, &card{n, s})
		}
	}
	d.shuffle()
	return d
}

func (d *deck) shuffle() {
	l := len(d.cs)
	s := make([]*card, l)
	for i, c := range d.cs {
		r := randInt(i + 1)
		s[i] = s[r]
		s[r] = c
	}
	d.cs = s
}

func (d *deck) getCard() *card {
	if len(d.cs) == 0 {
		return nil
	}
	c := d.cs[0]
	d.cs = d.cs[1:]
	return c
}

type card struct {
	n int
	s suit
}

func (c *card) String() string {
	return strconv.Itoa(c.n) + "/" + strconv.Itoa(int(c.s))
}

type suit int

const (
	noSuit suit = iota
	spades
	hearts
	diamonds
	clubs
)

type player struct {
	cs []*card
	ai ai
}

func newPlayer(aiName string) *player {
	return &player{nil, ais[aiName]}
}

func (p *player) addCard(c *card) {
	p.cs = append(p.cs, c)
}

func (p *player) play(top *card, asked suit, d *deck) (*card, suit) {
	i, s := p.ai(p.cs, top, asked, d)
	if i == -1 {
		return nil, noSuit
	}
	c := p.cs[i]
	p.cs = append(p.cs[:i], p.cs[i+1:]...)
	if c.n != 11 {
		return c, noSuit
	}
	return c, s
}

func (p *player) end() bool {
	return len(p.cs) == 0
}

func (p *player) String() string {
	return fmt.Sprintf("%v", p.cs)
}

// ai returns the index of the card in cs that should be played, -1 if there's
// no card to play, and suit if the card is a 11.
type ai func(cs []*card, top *card, asked suit, d *deck) (int, suit)

var ais = map[string]ai{
	"randomAI": randomAI,
	"onlyFirstAI": onlyFirstAI,
	"onlyBuyAI": onlyBuyAI,
}

func randomAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	return is[randInt(len(is))], suit(randInt(4) + 1)
}

func onlyFirstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	if cs[0].n == 11 {
		return 0, spades
	}
	if asked != noSuit {
		if cs[0].s == asked {
			return 0, noSuit
		}
		return -1, noSuit
	}
	if cs[0].n == top.n || cs[0].s == top.s {
		return 0, noSuit
	}
	return -1, noSuit
}

func onlyBuyAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	return -1, noSuit
}

func validIndexes(cs []*card, top *card, asked suit) []int {
	var is []int
	for i, c := range cs {
		if c.n == 11 {
			is = append(is, i)
			continue
		}
		if asked != noSuit {
			if c.s == asked {
				is = append(is, i)
			}
			continue
		}
		if c.s == top.s || c.n == top.n {
			is = append(is, i)
		}
	}
	return is
}

func isPlayValid(newc *card, newa suit, oldc *card, olda suit) {
	if newc.n == 11 {
		if newa == noSuit {
			log.Fatal("newc.n = 11, newa = noSuit")
		}
		return
	}
	if newa != noSuit {
		log.Fatalf("newc.n = %v, newa = %v", newc.n, newa)
	}
	if olda != noSuit {
		if newc.s != olda {
			log.Fatalf("newc.s = %v, olda = %v", newc.s, olda)
		}
		return
	}
	if newc.n != oldc.n && newc.s != newc.s {
		log.Fatalf("newc = %v, oldc = %v", newc, oldc)
	}
}

// randInt returns a random number from 0 to max - 1.
func randInt(max int) int {
	b, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
	if err != nil {
		log.Fatal(err)
	}
	return int(b.Int64())
}
<commit_msg>numGames=100<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"flag"
	"fmt"
	"log"
	"math"
	"math/big"
	"strconv"
)

var (
	startingCards = flag.Int("starting_cards", 5, "Number of cards each player should start with.")
	numGames      = flag.Int("num_games", 100, "Number of games that will be played.")
	numTests      = flag.Int("num_tests", 100, "Number of tests to be performed.")
	playerAI      = flag.String("player_ai", "randomAI", "AI to be used by the main player.")
	opponentAI    = flag.String("opponent_ai", "onlyFirstAI", "AI to be used by the opponent player.")
	randomStart   = flag.Bool("random_start", true, "Defines who starts randomly. If false, the first player always starts.")
	debug         = flag.Bool("debug", false, "Print debug information")
)

func main() {
	flag.Parse()
	var results []int
	total := 0
	for i := 0; i < *numTests; i++ {
		res := runGame()
		results = append(results, res)
		total += res
	}
	avg := float64(total) / float64(*numTests)
	variance := 0.0
	for _, res := range results {
		term := float64(res) - avg
		variance += term * term
	}
	variance /= float64(*numTests)
	fmt.Printf("%v+-%v\n", avg, math.Sqrt(variance))
}

func runGame() int {
	w := 0
	for i := 0; i < *numGames; i++ {
		g := newGame()
		if g.play() == 0 {
			w++
		}
	}
	return w
}

type game struct {
	players []*player
	playing int
	deck    *deck
	top     *card
	asked   suit
	order   int
	garbage []*card
}

func newGame() *game {
	g := &game{}
	if *randomStart {
		if *debug {
			fmt.Println("random_start")
		}
		g.playing = randInt(2)
	}
	g.deck = newDeck()
	g.players = []*player{newPlayer(*playerAI), newPlayer(*opponentAI)}
	for nc := 0; nc < *startingCards; nc++ {
		g.players[0].addCard(g.getCard())
		g.players[1].addCard(g.getCard())
	}
	g.top = g.getCard()
	g.order = 1
	return g
}

func (g *game) play() int {
	for {
		top, asked := g.player().play(g.top, g.asked, g.deck)
		if *debug {
			fmt.Println(g, top, asked)
		}
		if top == nil {
			g.addCard()
			g.next()
			continue
		}
		isPlayValid(top, asked, g.top, g.asked)
		if g.end() {
			return g.playing
		}
		g.garbage = append(g.garbage, g.top)
		g.top, g.asked = top, asked
		switch g.top.n {
		case 1:
		case 12:
			if len(g.players) == 2 {
				g.next()
			} else {
				g.order *= -1
			}
		case 7:
			g.next()
			g.addCard()
			g.addCard()
		}
		g.next()
	}
}

func (g *game) player() *player {
	return g.players[g.playing]
}

func (g *game) next() {
	g.playing = (g.playing + g.order + len(g.players)) % len(g.players)
}

func (g *game) getCard() *card {
	c := g.deck.getCard()
	if c != nil {
		return c
	}
	if g.garbage == nil {
		log.Fatal("empty garbage")
	}
	g.deck.cs = g.garbage
	g.deck.shuffle()
	g.garbage = nil
	return g.deck.getCard()
}

func (g *game) addCard() {
	g.player().addCard(g.getCard())
}

func (g *game) end() bool {
	return g.player().end()
}

func (g *game) String() string {
	var b bytes.Buffer
	b.WriteString(fmt.Sprintf("-> %v %v %v %v %v %v ", len(g.deck.cs), len(g.garbage), g.playing, g.top, g.asked, g.order))
	for _, p := range g.players {
		b.WriteString(p.String())
		b.WriteString(" ")
	}
	return b.String()
}

type deck struct {
	cs []*card
}

func newDeck() *deck {
	d := &deck{}
	for n := 1; n <= 13; n++ {
		for s := spades; s <= clubs; s++ {
			d.cs = append(d.cs, &card{n, s})
		}
	}
	d.shuffle()
	return d
}

func (d *deck) shuffle() {
	l := len(d.cs)
	s := make([]*card, l)
	for i, c := range d.cs {
		r := randInt(i + 1)
		s[i] = s[r]
		s[r] = c
	}
	d.cs = s
}

func (d *deck) getCard() *card {
	if len(d.cs) == 0 {
		return nil
	}
	c := d.cs[0]
	d.cs = d.cs[1:]
	return c
}

type card struct {
	n int
	s suit
}

func (c *card) String() string {
	return strconv.Itoa(c.n) + "/" + strconv.Itoa(int(c.s))
}

type suit int

const (
	noSuit suit = iota
	spades
	hearts
	diamonds
	clubs
)

type player struct {
	cs []*card
	ai ai
}

func newPlayer(aiName string) *player {
	return &player{nil, ais[aiName]}
}

func (p *player) addCard(c *card) {
	p.cs = append(p.cs, c)
}

func (p *player) play(top *card, asked suit, d *deck) (*card, suit) {
	i, s := p.ai(p.cs, top, asked, d)
	if i == -1 {
		return nil, noSuit
	}
	c := p.cs[i]
	p.cs = append(p.cs[:i], p.cs[i+1:]...)
	if c.n != 11 {
		return c, noSuit
	}
	return c, s
}

func (p *player) end() bool {
	return len(p.cs) == 0
}

func (p *player) String() string {
	return fmt.Sprintf("%v", p.cs)
}

// ai returns the index of the card in cs that should be played, -1 if there's
// no card to play, and suit if the card is a 11.
type ai func(cs []*card, top *card, asked suit, d *deck) (int, suit)

var ais = map[string]ai{
	"randomAI": randomAI,
	"onlyFirstAI": onlyFirstAI,
	"onlyBuyAI": onlyBuyAI,
}

func randomAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	return is[randInt(len(is))], suit(randInt(4) + 1)
}

func onlyFirstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	if cs[0].n == 11 {
		return 0, spades
	}
	if asked != noSuit {
		if cs[0].s == asked {
			return 0, noSuit
		}
		return -1, noSuit
	}
	if cs[0].n == top.n || cs[0].s == top.s {
		return 0, noSuit
	}
	return -1, noSuit
}

func onlyBuyAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	return -1, noSuit
}

func validIndexes(cs []*card, top *card, asked suit) []int {
	var is []int
	for i, c := range cs {
		if c.n == 11 {
			is = append(is, i)
			continue
		}
		if asked != noSuit {
			if c.s == asked {
				is = append(is, i)
			}
			continue
		}
		if c.s == top.s || c.n == top.n {
			is = append(is, i)
		}
	}
	return is
}

func isPlayValid(newc *card, newa suit, oldc *card, olda suit) {
	if newc.n == 11 {
		if newa == noSuit {
			log.Fatal("newc.n = 11, newa = noSuit")
		}
		return
	}
	if newa != noSuit {
		log.Fatalf("newc.n = %v, newa = %v", newc.n, newa)
	}
	if olda != noSuit {
		if newc.s != olda {
			log.Fatalf("newc.s = %v, olda = %v", newc.s, olda)
		}
		return
	}
	if newc.n != oldc.n && newc.s != newc.s {
		log.Fatalf("newc = %v, oldc = %v", newc, oldc)
	}
}

// randInt returns a random number from 0 to max - 1.
func randInt(max int) int {
	b, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
	if err != nil {
		log.Fatal(err)
	}
	return int(b.Int64())
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 idxmap provides mapping structure that allows to:
// - write & lookup data by indexes
// - index data using multiple indexes
// - watch data changes in the map
// - create local cache data of key value store (such as ETCD).
//
//  Primary Index                Item                                Secondary indexes
// ===================================================================================
//
//    Eth1              +---------------------+                 { "IP" : ["192.168.2.1", "10.0.0.8"],
//                      |  Status: Enabled    |                   "Type" : ["ethernet"]
//                      |  IP: 192.168.2.1    |                 }
//                      |      10.0.0.8       |
//                      |  Type: ethernet     |
//                      |  Desc: something    |
//                      +---------------------+
//
//
//
// Function `RegisterName` adds a value (item) into the mapping. In the
// function call the primary index(name) for the item is specified. The
// values of the primary index are unique, if the name already exists,
// then the item is overwritten. To retrieve an item identified by the
// primary index, use the `Lookup` function. An item can be removed from
// the mapping by calling the `UnregisterName` function. The names that
// are currently registered can be retrieved by calling the `ListNames`
// function.
//
// The constructor allows to define a `createIndexes` function that extracts
// secondary indices from stored items. The function returns a map indexed
// by names of secondary indexes, and the values are the extracted values
// for the particular item. The values of secondary indexes are not necessarily
// unique. To retrieve items based on secondary indicess use the
// `LookupByMetadata` function. In contrast to the lookup by primary index,
// the function may return multiple names.
//
// `Watch` allows to define a callback that is called when a change in the
// mapping occurs. There is a helper function `ToChan` available, which allows
// to deliver notifications through a channel.
package idxmap
<commit_msg>Update doc.go<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 idxmap provides mapping structure that allows to:
// - write & lookup data by indexes
// - index data using multiple indexes
// - watch data changes in the map
// - create local cache data of key value store (such as etcd).
//
//  Primary Index                Item                                Secondary indexes
// ===================================================================================
//
//    Eth1              +---------------------+                 { "IP" : ["192.168.2.1", "10.0.0.8"],
//                      |  Status: Enabled    |                   "Type" : ["ethernet"]
//                      |  IP: 192.168.2.1    |                 }
//                      |      10.0.0.8       |
//                      |  Type: ethernet     |
//                      |  Desc: something    |
//                      +---------------------+
//
//
//
// Function `RegisterName` adds a value (item) into the mapping. In the
// function call the primary index(name) for the item is specified. The
// values of the primary index are unique, if the name already exists,
// then the item is overwritten. To retrieve an item identified by the
// primary index, use the `Lookup` function. An item can be removed from
// the mapping by calling the `UnregisterName` function. The names that
// are currently registered can be retrieved by calling the `ListNames`
// function.
//
// The constructor allows you to define a `createIndexes` function that extracts
// secondary indices from stored items. The function returns a map indexed
// by names of secondary indexes, and the values are the extracted values
// for the particular item. The values of secondary indexes are not necessarily
// unique. To retrieve items based on secondary indices use the
// `LookupByMetadata` function. In contrast to the lookup by primary index,
// the function may return multiple names.
//
// `Watch` allows to define a callback that is called when a change in the
// mapping occurs. There is a helper function `ToChan` available, which allows
// to deliver notifications through a channel.
package idxmap
<|endoftext|>


<commit_before><commit_msg>Remove go-kit http error<commit_after><|endoftext|>