<commit_before>package broker

import (
	"time"

	"github.com/kr/beanstalk"
)

const (
	// ListTubeDelay is the time between sending list-tube to beanstalkd
	// to discover and watch newly created tubes.
	ListTubeDelay = 10 * time.Second
)

type BrokerDispatcher struct {
	address string
	cmd     string
	conn    *beanstalk.Conn
	perTube uint64
	tubeSet map[string]bool
}

func NewBrokerDispatcher(address, cmd string, perTube uint64) *BrokerDispatcher {
	return &BrokerDispatcher{
		address: address,
		cmd:     cmd,
		perTube: perTube,
		tubeSet: make(map[string]bool),
	}
}

// RunTube runs broker(s) for the specified tube.
func (bd *BrokerDispatcher) RunTube(tube string) {
	bd.tubeSet[tube] = true
	for i := uint64(0); i < bd.perTube; i++ {
		bd.runBroker(tube, i)
	}
}

// RunTube runs a broker for the specified tubes.
func (bd *BrokerDispatcher) RunTubes(tubes []string) {
	for _, tube := range tubes {
		bd.RunTube(tube)
	}
}

// RunAllTubes polls beanstalkd, running a broker as new tubes are created.
func (bd *BrokerDispatcher) RunAllTubes() (err error) {
	conn, err := beanstalk.Dial("tcp", bd.address)
	if err == nil {
		bd.conn = conn
	} else {
		return
	}

	go func() {
		ticker := time.Tick(ListTubeDelay)
		for _ = range ticker {
			if e := bd.watchNewTubes(); e != nil {
				// ignore error
			}
		}
	}()

	return
}

func (bd *BrokerDispatcher) runBroker(tube string, slot uint64) {
	go func() {
		b := New(bd.address, tube, slot, bd.cmd, nil)
		b.Run(nil)
	}()
}

func (bd *BrokerDispatcher) watchNewTubes() (err error) {
	tubes, err := bd.conn.ListTubes()
	if err != nil {
		return
	}

	for _, tube := range tubes {
		if !bd.tubeSet[tube] {
			bd.RunTube(tube)
		}
	}

	return
}
<commit_msg>BrokerDispatcher documentation improved.<commit_after>package broker

import (
	"time"

	"github.com/kr/beanstalk"
)

const (
	// ListTubeDelay is the time between sending list-tube to beanstalkd
	// to discover and watch newly created tubes.
	ListTubeDelay = 10 * time.Second
)

// BrokerDispatcher manages the running of Broker instances for tubes.  It can
// be manually told tubes to start, or it can poll for tubes as they are
// created. The `perTube` option determines how many brokers are started for
// each tube.
type BrokerDispatcher struct {
	address string
	cmd     string
	conn    *beanstalk.Conn
	perTube uint64
	tubeSet map[string]bool
}

func NewBrokerDispatcher(address, cmd string, perTube uint64) *BrokerDispatcher {
	return &BrokerDispatcher{
		address: address,
		cmd:     cmd,
		perTube: perTube,
		tubeSet: make(map[string]bool),
	}
}

// RunTube runs broker(s) for the specified tube.
// The number of brokers started is determined by the perTube argument to
// NewBrokerDispatcher.
func (bd *BrokerDispatcher) RunTube(tube string) {
	bd.tubeSet[tube] = true
	for i := uint64(0); i < bd.perTube; i++ {
		bd.runBroker(tube, i)
	}
}

// RunTube runs brokers for the specified tubes.
func (bd *BrokerDispatcher) RunTubes(tubes []string) {
	for _, tube := range tubes {
		bd.RunTube(tube)
	}
}

// RunAllTubes polls beanstalkd, running broker as new tubes are created.
func (bd *BrokerDispatcher) RunAllTubes() (err error) {
	conn, err := beanstalk.Dial("tcp", bd.address)
	if err == nil {
		bd.conn = conn
	} else {
		return
	}

	go func() {
		ticker := time.Tick(ListTubeDelay)
		for _ = range ticker {
			if e := bd.watchNewTubes(); e != nil {
				// ignore error
			}
		}
	}()

	return
}

func (bd *BrokerDispatcher) runBroker(tube string, slot uint64) {
	go func() {
		b := New(bd.address, tube, slot, bd.cmd, nil)
		b.Run(nil)
	}()
}

func (bd *BrokerDispatcher) watchNewTubes() (err error) {
	tubes, err := bd.conn.ListTubes()
	if err != nil {
		return
	}

	for _, tube := range tubes {
		if !bd.tubeSet[tube] {
			bd.RunTube(tube)
		}
	}

	return
}
<|endoftext|>


<commit_before>package main

// DNSDeleteCmd arguments.
type DNSDeleteCmd struct {
	A     DNSDeleteACmd     `command:"a" description:"Delete an A record." alias:"A"`
	AAAA  DNSDeleteAAAACmd  `command:"aaaa" description:"Delete an AAAA record." alias:"AAAA"`
	CNAME DNSDeleteCNAMECmd `command:"cname" description:"Delete a CNAME record." alias:"CNAME"`
	MX    DNSDeleteMXCmd    `command:"mx" description:"Delete an MX record." alias:"MX"`
	NS    DNSDeleteNSCmd    `command:"ns" description:"Delete an NS record." alias:"NS"`
	TXT   DNSDeleteTXTCmd   `command:"txt" description:"Delete a TXT record." alias:"TXT"`
	SRV   DNSDeleteSRVCmd   `command:"srv" description:"Delete a SRV record." alias:"SRV"`
}

// DNSDeleteACmd arguments.
type DNSDeleteACmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteACmd deletes an A record.
func (cmd *DNSDeleteACmd) Execute(args []string) error {
	err := client.DeleteARecord(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host, false)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteAAAACmd arguments.
type DNSDeleteAAAACmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteAAAACmd deletes an AAAA record.
func (cmd *DNSDeleteAAAACmd) Execute(args []string) error {
	err := client.DeleteARecord(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host, true)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteCNAMECmd arguments.
type DNSDeleteCNAMECmd struct {
	Args DNSDeleteArgsAll `positional-args:"true"`
}

// DNSDeleteCNAMECmd deletes a CNAME record.
func (cmd *DNSDeleteCNAMECmd) Execute(args []string) error {
	err := client.DeleteCNAME(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteMXCmd arguments.
type DNSDeleteMXCmd struct {
	Args DNSDeleteArgsAll `positional-args:"true"`
}

// DNSDeleteMXCmd deletes a MX record.
func (cmd *DNSDeleteMXCmd) Execute(args []string) error {
	err := client.DeleteMX(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteNSCmd arguments.
type DNSDeleteNSCmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteTXTCmd arguments.
type DNSDeleteTXTCmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteSRVCmd arguments.
type DNSDeleteSRVCmd struct {
	Args struct {
		Domain string `required:"true" positional-arg-name:"DOMAIN" description:"Domain name."`
		Value  string `required:"true" positional-arg-name:"VALUE" description:"IP address, or FQDN for CNAME/MX etc."`
		Host   string `positional-arg-name:"HOST" description:"Host name."`
		Port   int    `positional-arg-name:"PORT" description:"Port number of the service."`
		Weight int    `positional-arg-name:"WEIGHT" description:"Weight of the service."`
	} `positional-args:"true"`
}
<commit_msg>All DNS record deletion implemented in lbmgr.<commit_after>package main

// DNSDeleteCmd arguments.
type DNSDeleteCmd struct {
	A     DNSDeleteACmd     `command:"a" description:"Delete an A record." alias:"A"`
	AAAA  DNSDeleteAAAACmd  `command:"aaaa" description:"Delete an AAAA record." alias:"AAAA"`
	CNAME DNSDeleteCNAMECmd `command:"cname" description:"Delete a CNAME record." alias:"CNAME"`
	MX    DNSDeleteMXCmd    `command:"mx" description:"Delete an MX record." alias:"MX"`
	NS    DNSDeleteNSCmd    `command:"ns" description:"Delete an NS record." alias:"NS"`
	TXT   DNSDeleteTXTCmd   `command:"txt" description:"Delete a TXT record." alias:"TXT"`
	SRV   DNSDeleteSRVCmd   `command:"srv" description:"Delete a SRV record." alias:"SRV"`
}

// DNSDeleteACmd arguments.
type DNSDeleteACmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteACmd deletes an A record.
func (cmd *DNSDeleteACmd) Execute(args []string) error {
	err := client.DeleteARecord(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host, false)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteAAAACmd arguments.
type DNSDeleteAAAACmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteAAAACmd deletes an AAAA record.
func (cmd *DNSDeleteAAAACmd) Execute(args []string) error {
	err := client.DeleteARecord(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host, true)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteCNAMECmd arguments.
type DNSDeleteCNAMECmd struct {
	Args DNSDeleteArgsAll `positional-args:"true"`
}

// DNSDeleteCNAMECmd deletes a CNAME record.
func (cmd *DNSDeleteCNAMECmd) Execute(args []string) error {
	err := client.DeleteCNAME(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteMXCmd arguments.
type DNSDeleteMXCmd struct {
	Args DNSDeleteArgsAll `positional-args:"true"`
}

// DNSDeleteMXCmd deletes an MX record.
func (cmd *DNSDeleteMXCmd) Execute(args []string) error {
	err := client.DeleteMX(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteNSCmd arguments.
type DNSDeleteNSCmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteNSCmd deletes an NS record.
func (cmd *DNSDeleteNSCmd) Execute(args []string) error {
	err := client.DeleteNS(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteTXTCmd arguments.
type DNSDeleteTXTCmd struct {
	Args DNSDeleteArgs `positional-args:"true"`
}

// DNSDeleteTXTCmd deletes a TXT record.
func (cmd *DNSDeleteTXTCmd) Execute(args []string) error {
	err := client.DeleteTXT(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}

// DNSDeleteSRVCmd arguments.
type DNSDeleteSRVCmd struct {
	Args struct {
		Domain string `required:"true" positional-arg-name:"DOMAIN" description:"Domain name."`
		Value  string `required:"true" positional-arg-name:"VALUE" description:"IP address, or FQDN for CNAME/MX etc."`
		Host   string `positional-arg-name:"HOST" description:"Host name."`
		Port   int    `positional-arg-name:"PORT" description:"Port number of the service."`
		Weight int    `positional-arg-name:"WEIGHT" description:"Weight of the service."`
	} `positional-args:"true"`
}

// DNSDeleteSRVCmd deletes a SRV record.
func (cmd *DNSDeleteSRVCmd) Execute(args []string) error {
	err := client.DeleteSRV(cmd.Args.Domain, cmd.Args.Value, cmd.Args.Host, cmd.Args.Port, cmd.Args.Weight)
	if err != nil {
		return err
	}

	pr("Record deleted.")
	return nil
}
<|endoftext|>


<commit_before>package network

import (
	"sort"
	"time"

	"github.com/golang/glog"

	"github.com/BotBotMe/botbot-bot/common"
	"github.com/BotBotMe/botbot-bot/line"
	"github.com/BotBotMe/botbot-bot/network/irc"
)

type NetworkManager struct {
	chatbots   map[int]common.ChatBot
	fromServer chan *line.Line
	storage    common.Storage
	isRunning  bool
}

func NewNetworkManager(storage common.Storage, fromServer chan *line.Line) *NetworkManager {

	netMan := &NetworkManager{
		chatbots:   make(map[int]common.ChatBot),
		fromServer: fromServer,
		storage:    storage,
		isRunning:  true,
	}

	return netMan
}

// Get the User for a ChatbotId
func (self *NetworkManager) GetUserByChatbotId(id int) string {
	return self.getChatbotById(id).GetUser()
}

// Connect to networks / start chatbots. Loads chatbot configuration from DB.
func (self *NetworkManager) RefreshChatbots() {
	if glog.V(2) {
		glog.Infoln("Entering in NetworkManager.RefreshChatbots")
	}

	// Sleeping before refreshing chatbots to make sure that the change made
	// made it into postgres. This illustrate the fact that redis is faster
	// than postgres
	time.Sleep(1 * time.Second)

	botConfigs := self.storage.BotConfig()

	var current common.ChatBot
	var id int
	active := make(sort.IntSlice, 0)

	// Create new ones
	for _, config := range botConfigs {
		id = config.Id
		active = append(active, id)

		current = self.chatbots[id]
		if current == nil {
			// Create
			if glog.V(2) {
				glog.Infoln("Connect the bot with the following config:", config)
			}
			self.chatbots[id] = self.Connect(config)
		} else {
			// Update
			if glog.V(2) {
				glog.Infoln("Update the bot with the following config:", config)
			}
			self.chatbots[id].Update(config)
		}

	}

	// Stop old ones

	active.Sort()
	numActive := len(active)

	for currId, _ := range self.chatbots {

		if active.Search(currId) == numActive { // if currId not in active:
			glog.Infoln("Stopping chatbot: ", currId)

			self.chatbots[currId].Close()
			delete(self.chatbots, currId)
		}
	}
	if glog.V(2) {
		glog.Infoln("Exiting NetworkManager.RefreshChatbots")
	}

}

func (self *NetworkManager) Connect(config *common.BotConfig) common.ChatBot {

	glog.Infoln("Creating chatbot: %+v\n", config)
	return irc.NewBot(config, self.fromServer)
}

func (self *NetworkManager) Send(chatbotId int, channel, msg string) {
	self.chatbots[chatbotId].Send(channel, msg)
}

// Check out chatbots are alive, recreating them if not. Run this in go-routine.
func (self *NetworkManager) MonitorChatbots() {

	for self.isRunning {
		for id, bot := range self.chatbots {
			if !bot.IsRunning() {
				self.restart(id)
			}
		}
		time.Sleep(1 * time.Second)
	}
}

// get a chatbot by id
func (self *NetworkManager) getChatbotById(id int) common.ChatBot {
	return self.chatbots[id]
}

// Restart a chatbot
func (self *NetworkManager) restart(botId int) {

	glog.Infoln("Restarting bot ", botId)

	var config *common.BotConfig

	// Find configuration for this bot

	botConfigs := self.storage.BotConfig()
	for _, botConf := range botConfigs {
		if botConf.Id == botId {
			config = botConf
			break
		}
	}

	if config == nil {
		glog.Infoln("Could not find configuration for bot ", botId, ". Bot will not run.")
		delete(self.chatbots, botId)
		return
	}

	self.chatbots[botId] = self.Connect(config)
}

// Stop all bots
func (self *NetworkManager) Shutdown() {
	self.isRunning = false
	for _, bot := range self.chatbots {
		bot.Close()
	}
}
<commit_msg>Move the sleep to avoid the race condition from botbot-bot to botbot-web<commit_after>package network

import (
	"sort"
	"time"

	"github.com/golang/glog"

	"github.com/BotBotMe/botbot-bot/common"
	"github.com/BotBotMe/botbot-bot/line"
	"github.com/BotBotMe/botbot-bot/network/irc"
)

type NetworkManager struct {
	chatbots   map[int]common.ChatBot
	fromServer chan *line.Line
	storage    common.Storage
	isRunning  bool
}

func NewNetworkManager(storage common.Storage, fromServer chan *line.Line) *NetworkManager {

	netMan := &NetworkManager{
		chatbots:   make(map[int]common.ChatBot),
		fromServer: fromServer,
		storage:    storage,
		isRunning:  true,
	}

	return netMan
}

// Get the User for a ChatbotId
func (self *NetworkManager) GetUserByChatbotId(id int) string {
	return self.getChatbotById(id).GetUser()
}

// Connect to networks / start chatbots. Loads chatbot configuration from DB.
func (self *NetworkManager) RefreshChatbots() {
	if glog.V(2) {
		glog.Infoln("Entering in NetworkManager.RefreshChatbots")
	}

	botConfigs := self.storage.BotConfig()

	var current common.ChatBot
	var id int
	active := make(sort.IntSlice, 0)

	// Create new ones
	for _, config := range botConfigs {
		id = config.Id
		active = append(active, id)

		current = self.chatbots[id]
		if current == nil {
			// Create
			if glog.V(2) {
				glog.Infoln("Connect the bot with the following config:", config)
			}
			self.chatbots[id] = self.Connect(config)
		} else {
			// Update
			if glog.V(2) {
				glog.Infoln("Update the bot with the following config:", config)
			}
			self.chatbots[id].Update(config)
		}

	}

	// Stop old ones

	active.Sort()
	numActive := len(active)

	for currId, _ := range self.chatbots {

		if active.Search(currId) == numActive { // if currId not in active:
			glog.Infoln("Stopping chatbot: ", currId)

			self.chatbots[currId].Close()
			delete(self.chatbots, currId)
		}
	}
	if glog.V(2) {
		glog.Infoln("Exiting NetworkManager.RefreshChatbots")
	}

}

func (self *NetworkManager) Connect(config *common.BotConfig) common.ChatBot {

	glog.Infoln("Creating chatbot: %+v\n", config)
	return irc.NewBot(config, self.fromServer)
}

func (self *NetworkManager) Send(chatbotId int, channel, msg string) {
	self.chatbots[chatbotId].Send(channel, msg)
}

// Check out chatbots are alive, recreating them if not. Run this in go-routine.
func (self *NetworkManager) MonitorChatbots() {

	for self.isRunning {
		for id, bot := range self.chatbots {
			if !bot.IsRunning() {
				self.restart(id)
			}
		}
		time.Sleep(1 * time.Second)
	}
}

// get a chatbot by id
func (self *NetworkManager) getChatbotById(id int) common.ChatBot {
	return self.chatbots[id]
}

// Restart a chatbot
func (self *NetworkManager) restart(botId int) {

	glog.Infoln("Restarting bot ", botId)

	var config *common.BotConfig

	// Find configuration for this bot

	botConfigs := self.storage.BotConfig()
	for _, botConf := range botConfigs {
		if botConf.Id == botId {
			config = botConf
			break
		}
	}

	if config == nil {
		glog.Infoln("Could not find configuration for bot ", botId, ". Bot will not run.")
		delete(self.chatbots, botId)
		return
	}

	self.chatbots[botId] = self.Connect(config)
}

// Stop all bots
func (self *NetworkManager) Shutdown() {
	self.isRunning = false
	for _, bot := range self.chatbots {
		bot.Close()
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"

	"github.com/codegangsta/cli"
	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
)

const msgExplainFetch = "In order to fetch a specific assignment, call the fetch command with a specific assignment.\n\nexercism fetch ruby matrix"

// List returns the full list of assignments for a given track.
func List(ctx *cli.Context) {
	c, err := config.New(ctx.GlobalString("config"))
	if err != nil {
		log.Fatal(err)
	}
	args := ctx.Args()

	if len(args) != 1 {
		msg := "Usage: exercism list LANGUAGE"
		log.Fatal(msg)
	}

	language := args[0]
	client := api.NewClient(c)
	problems, err := client.List(language)
	if err != nil {
		if err == api.ErrUnknownLanguage {
			log.Fatalf("The requested language '%s' is unknown", language)
		}
		log.Fatal(err)
	}

	for _, p := range problems {
		fmt.Printf("%s\n", p)
	}
	fmt.Printf("\n%s\n\n", msgExplainFetch)
}
<commit_msg>Refer to track correctly in user-facing error message<commit_after>package cmd

import (
	"fmt"
	"log"

	"github.com/codegangsta/cli"
	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
)

const msgExplainFetch = "In order to fetch a specific assignment, call the fetch command with a specific assignment.\n\nexercism fetch ruby matrix"

// List returns the full list of assignments for a given track.
func List(ctx *cli.Context) {
	c, err := config.New(ctx.GlobalString("config"))
	if err != nil {
		log.Fatal(err)
	}
	args := ctx.Args()

	if len(args) != 1 {
		msg := "Usage: exercism list LANGUAGE"
		log.Fatal(msg)
	}

	language := args[0]
	client := api.NewClient(c)
	problems, err := client.List(language)
	if err != nil {
		if err == api.ErrUnknownLanguage {
			log.Fatalf("There is no track with ID '%s'.", language)
		}
		log.Fatal(err)
	}

	for _, p := range problems {
		fmt.Printf("%s\n", p)
	}
	fmt.Printf("\n%s\n\n", msgExplainFetch)
}
<|endoftext|>


<commit_before>package tarantool

import (
	"bufio"
	"errors"
	"io"
	"net"
	"net/url"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	ErrEmptyDefaultSpace = errors.New("zero-length default space or unnecessary slash in dsn.path")
	ErrSyncFailed        = errors.New("SYNC failed")
)

type Options struct {
	ConnectTimeout time.Duration
	QueryTimeout   time.Duration
	DefaultSpace   string
	User           string
	Password       string
	UUID           string
	ReplicaSetUUID string
	Perf           PerfCount

	// PoolMaxPacketSize describes maximum size of packet buffer
	// that can be added to packet pool.
	// If the packet size is 0, option is ignored.
	PoolMaxPacketSize int
}

type Greeting struct {
	Version []byte
	Auth    []byte
}

type Connection struct {
	requestID uint64
	requests  *requestMap
	writeChan chan *request // packed messages with header
	closeOnce sync.Once
	exit      chan bool
	closed    chan bool
	tcpConn   net.Conn

	ccr io.Reader
	ccw io.Writer

	// options
	queryTimeout      time.Duration
	greeting          *Greeting
	packData          *packData
	remoteAddr        string
	firstError        error
	firstErrorLock    *sync.Mutex
	perf              PerfCount
	poolMaxPacketSize int
}

// Connect to tarantool instance with options.
// Returned Connection could be used to execute queries.
func Connect(dsnString string, options *Options) (conn *Connection, err error) {
	var opts Options
	if options != nil {
		opts = *options
	}
	dsn, opts, err := parseOptions(dsnString, opts)
	if err != nil {
		return nil, err
	}
	return connect(dsn.Scheme, dsn.Host, opts)
}

func connect(scheme, addr string, opts Options) (conn *Connection, err error) {
	conn, err = newConn(scheme, addr, opts)
	if err != nil {
		return
	}

	// set schema pulling deadline
	deadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(deadline)

	err = conn.pullSchema()
	if err != nil {
		conn.tcpConn.Close()
		conn = nil
		return
	}

	// remove deadline
	conn.tcpConn.SetDeadline(time.Time{})

	go conn.worker()

	return
}

func newConn(scheme, addr string, opts Options) (conn *Connection, err error) {

	defer func() { // close opened connection if error
		if err != nil && conn != nil {
			if conn.tcpConn != nil {
				conn.tcpConn.Close()
			}
			conn = nil
		}
	}()

	conn = &Connection{
		remoteAddr:        addr,
		requests:          newRequestMap(),
		writeChan:         make(chan *request, 256),
		exit:              make(chan bool),
		closed:            make(chan bool),
		firstErrorLock:    &sync.Mutex{},
		packData:          newPackData(opts.DefaultSpace),
		queryTimeout:      opts.QueryTimeout,
		perf:              opts.Perf,
		poolMaxPacketSize: opts.PoolMaxPacketSize,
	}

	conn.tcpConn, err = net.DialTimeout(scheme, conn.remoteAddr, opts.ConnectTimeout)
	if err != nil {
		return nil, err
	}

	if conn.perf.NetRead != nil {
		conn.ccr = NewCountedReader(conn.tcpConn, conn.perf.NetRead)
	} else {
		conn.ccr = conn.tcpConn
	}

	if conn.perf.NetWrite != nil {
		conn.ccw = NewCountedWriter(conn.tcpConn, conn.perf.NetWrite)
	} else {
		conn.ccw = conn.tcpConn
	}

	greeting := make([]byte, 128)

	connectDeadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(connectDeadline)
	// removing deadline deferred
	defer conn.tcpConn.SetDeadline(time.Time{})

	_, err = io.ReadFull(conn.ccr, greeting)
	if err != nil {
		return
	}

	conn.greeting = &Greeting{
		Version: greeting[:64],
		Auth:    greeting[64:108],
	}

	// try to authenticate if user have been provided
	if len(opts.User) > 0 {
		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)

		err = pp.packMsg(&Auth{
			User:         opts.User,
			Password:     opts.Password,
			GreetingAuth: conn.greeting.Auth,
		}, conn.packData)
		if err != nil {
			conn.releasePacket(pp)
			return
		}

		_, err = pp.WriteTo(conn.ccw)
		conn.releasePacket(pp)
		if err != nil {
			return
		}

		pp = packetPool.Get()
		defer conn.releasePacket(pp)

		if err = pp.readPacket(conn.ccr); err != nil {
			return
		}

		authResponse := &pp.packet
		if authResponse.requestID != requestID {
			err = ErrSyncFailed
			return
		}

		if authResponse.Result != nil && authResponse.Result.Error != nil {
			err = authResponse.Result.Error
			return
		}
	}

	return
}

func parseOptions(dsnString string, opts Options) (*url.URL, Options, error) {
	// remove schema, if present
	// === for backward compatibility (only tcp despite of user wishes :)
	dsnString = strings.TrimPrefix(dsnString, "unix:")
	// ===

	// tcp is the default scheme
	switch {
	case strings.HasPrefix(dsnString, "tcp://"):
	case strings.HasPrefix(dsnString, "//"):
		dsnString = "tcp:" + dsnString
	default:
		dsnString = "tcp://" + dsnString
	}
	dsn, err := url.Parse(dsnString)
	if err != nil {
		return dsn, opts, err
	}

	if len(opts.User) == 0 {
		if user := dsn.User; user != nil {
			opts.User = user.Username()
			opts.Password, _ = user.Password()
		}
	}

	if len(opts.DefaultSpace) == 0 && len(dsn.Path) > 0 {
		path := strings.TrimPrefix(dsn.Path, "/")
		// check it if it is necessary
		switch {
		case len(path) == 0:
			return nil, opts, ErrEmptyDefaultSpace
		//case strings.IndexAny(path, "/ ,") != -1:
		//	return nil, opts, ErrBadDSNPath
		default:
			opts.DefaultSpace = path
		}
	}

	if opts.ConnectTimeout.Nanoseconds() == 0 {
		opts.ConnectTimeout = DefaultConnectTimeout
	}
	if opts.QueryTimeout.Nanoseconds() == 0 {
		opts.QueryTimeout = DefaultQueryTimeout
	}

	return dsn, opts, nil
}

func (conn *Connection) pullSchema() (err error) {
	// select space and index schema
	request := func(q Query) (*Result, error) {
		var err error

		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)
		if err = pp.packMsg(q, conn.packData); err != nil {
			conn.releasePacket(pp)
			return nil, err
		}

		_, err = pp.WriteTo(conn.ccw)
		conn.releasePacket(pp)
		if err != nil {
			return nil, err
		}

		pp = packetPool.Get()
		defer conn.releasePacket(pp)

		if err = pp.readPacket(conn.ccr); err != nil {
			return nil, err
		}

		response := &pp.packet
		if response.requestID != requestID {
			return nil, errors.New("Bad response requestID")
		}

		if response.Result == nil {
			return nil, errors.New("Nil response result")
		}

		if response.Result.Error != nil {
			return nil, response.Result.Error
		}

		return response.Result, nil
	}

	res, err := request(&Select{
		Space:    ViewSpace,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, space := range res.Data {
		spaceID, _ := conn.packData.spaceNo(space[0])
		conn.packData.spaceMap[space[2].(string)] = spaceID
	}

	res, err = request(&Select{
		Space:    ViewIndex,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, index := range res.Data {
		spaceID, _ := conn.packData.fieldNo(index[0])
		indexID, _ := conn.packData.fieldNo(index[1])
		indexName := index[2].(string)
		indexAttr := index[4].(map[string]interface{}) // e.g: {"unique": true}
		indexFields := index[5].([]interface{})        // e.g: [[0 num] [1 str]]

		indexSpaceMap, exists := conn.packData.indexMap[spaceID]
		if !exists {
			indexSpaceMap = make(map[string]uint64)
			conn.packData.indexMap[spaceID] = indexSpaceMap
		}
		indexSpaceMap[indexName] = indexID

		// build list of primary key field numbers for this space, if the PK is detected
		if indexAttr != nil && indexID == 0 {
			if unique, ok := indexAttr["unique"]; ok && unique.(bool) {
				pk := make([]int, len(indexFields))
				for i := range indexFields {
					descr := indexFields[i].([]interface{})
					f, _ := conn.packData.fieldNo(descr[0])
					pk[i] = int(f)
				}
				conn.packData.primaryKeyMap[spaceID] = pk
			}
		}
	}

	return
}

func (conn *Connection) nextID() uint64 {
	return atomic.AddUint64(&conn.requestID, 1)
}

func (conn *Connection) stop() {
	conn.closeOnce.Do(func() {
		// debug.PrintStack()
		close(conn.exit)
		conn.tcpConn.Close()
		runtime.GC()
	})
}

func (conn *Connection) GetPerf() PerfCount {
	return conn.perf
}

func (conn *Connection) GetPrimaryKeyFields(space interface{}) ([]int, bool) {
	var spaceID uint64
	var err error

	if conn.packData == nil {
		return nil, false
	}
	if spaceID, err = conn.packData.spaceNo(space); err != nil {
		return nil, false
	}

	f, ok := conn.packData.primaryKeyMap[spaceID]
	return f, ok
}

func (conn *Connection) Close() {
	conn.stop()
	<-conn.closed
}

func (conn *Connection) String() string {
	return conn.remoteAddr
}

func (conn *Connection) IsClosed() bool {
	select {
	case <-conn.exit:
		return true
	default:
		return false
	}
}

func (conn *Connection) releasePacket(pp *BinaryPacket) {
	if conn.poolMaxPacketSize < cap(pp.body) || conn.poolMaxPacketSize == 0 {
		pp.Release()
	}
}

func (conn *Connection) getError() error {
	conn.firstErrorLock.Lock()
	defer conn.firstErrorLock.Unlock()
	return conn.firstError
}

func (conn *Connection) setError(err error) {
	if err != nil && err != io.EOF {
		conn.firstErrorLock.Lock()
		if conn.firstError == nil {
			conn.firstError = err
		}
		conn.firstErrorLock.Unlock()
	}
}

func (conn *Connection) worker() {
	var wg sync.WaitGroup

	wg.Add(2)

	go func() {
		err := conn.writer()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	go func() {
		err := conn.reader()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	wg.Wait()

	// release all pending packets
	writeChan := conn.writeChan

CLEANUP_LOOP:
	for {
		select {
		case req := <-writeChan:
			pp := req.packet
			if pp != nil {
				req.packet = nil
				conn.releasePacket(pp)
			}
		default:
			break CLEANUP_LOOP
		}
	}

	// send error reply to all pending requests
	conn.requests.CleanUp(func(req *request) {
		select {
		case req.replyChan <- &AsyncResult{
			Error:     ConnectionClosedError(conn),
			ErrorCode: ErrNoConnection,
			Opaque:    req.opaque,
		}:
		default:
		}
		requestPool.Put(req)
	})

	close(conn.closed)
}

func (conn *Connection) writer() (err error) {
	writeChan := conn.writeChan
	stopChan := conn.exit
	w := bufio.NewWriterSize(conn.ccw, DefaultWriterBufSize)

	wr := func(w io.Writer, req *request) error {
		packet := req.packet

		if conn.perf.NetPacketsOut != nil {
			conn.perf.NetPacketsOut.Add(1)
		}
		if conn.perf.QueryComplete != nil && req.opaque != nil {
			req.startedAt = time.Now()
		}

		_, err := packet.WriteTo(w)
		req.packet = nil
		conn.releasePacket(packet)
		return err
	}

WRITER_LOOP:
	for {
		select {
		case req, ok := <-writeChan:
			if !ok {
				break WRITER_LOOP
			}
			if err = wr(w, req); err != nil {
				break WRITER_LOOP
			}
		case <-stopChan:
			break WRITER_LOOP
		default:
			if err = w.Flush(); err != nil {
				break WRITER_LOOP
			}

			// same without flush
			select {
			case req, ok := <-writeChan:
				if !ok {
					break WRITER_LOOP
				}
				if err = wr(w, req); err != nil {
					break WRITER_LOOP
				}
			case <-stopChan:
				break WRITER_LOOP
			}
		}
	}

	return
}

func (conn *Connection) reader() (err error) {
	var pp *BinaryPacket
	var requestID uint64

	r := bufio.NewReaderSize(conn.ccr, DefaultReaderBufSize)

READER_LOOP:
	for {
		pp := packetPool.Get()
		if requestID, err = pp.readRawPacket(r); err != nil {
			break READER_LOOP
		}

		if conn.perf.NetPacketsIn != nil {
			conn.perf.NetPacketsIn.Add(1)
		}

		req := conn.requests.Pop(requestID)
		if req == nil {
			conn.releasePacket(pp)
			pp = nil
			continue
		}

		if conn.perf.QueryComplete != nil && req.opaque != nil {
			conn.perf.QueryComplete(req.opaque, time.Since(req.startedAt))
		}

		select {
		case req.replyChan <- &AsyncResult{0, nil, pp, conn, req.opaque}:
			pp = nil
		default:
		}

		requestPool.Put(req)
	}

	if pp != nil {
		conn.releasePacket(pp)
	}
	return
}
<commit_msg>[connection] swap releasePacket condition<commit_after>package tarantool

import (
	"bufio"
	"errors"
	"io"
	"net"
	"net/url"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	ErrEmptyDefaultSpace = errors.New("zero-length default space or unnecessary slash in dsn.path")
	ErrSyncFailed        = errors.New("SYNC failed")
)

type Options struct {
	ConnectTimeout time.Duration
	QueryTimeout   time.Duration
	DefaultSpace   string
	User           string
	Password       string
	UUID           string
	ReplicaSetUUID string
	Perf           PerfCount

	// PoolMaxPacketSize describes maximum size of packet buffer
	// that can be added to packet pool.
	// If the packet size is 0, option is ignored.
	PoolMaxPacketSize int
}

type Greeting struct {
	Version []byte
	Auth    []byte
}

type Connection struct {
	requestID uint64
	requests  *requestMap
	writeChan chan *request // packed messages with header
	closeOnce sync.Once
	exit      chan bool
	closed    chan bool
	tcpConn   net.Conn

	ccr io.Reader
	ccw io.Writer

	// options
	queryTimeout      time.Duration
	greeting          *Greeting
	packData          *packData
	remoteAddr        string
	firstError        error
	firstErrorLock    *sync.Mutex
	perf              PerfCount
	poolMaxPacketSize int
}

// Connect to tarantool instance with options.
// Returned Connection could be used to execute queries.
func Connect(dsnString string, options *Options) (conn *Connection, err error) {
	var opts Options
	if options != nil {
		opts = *options
	}
	dsn, opts, err := parseOptions(dsnString, opts)
	if err != nil {
		return nil, err
	}
	return connect(dsn.Scheme, dsn.Host, opts)
}

func connect(scheme, addr string, opts Options) (conn *Connection, err error) {
	conn, err = newConn(scheme, addr, opts)
	if err != nil {
		return
	}

	// set schema pulling deadline
	deadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(deadline)

	err = conn.pullSchema()
	if err != nil {
		conn.tcpConn.Close()
		conn = nil
		return
	}

	// remove deadline
	conn.tcpConn.SetDeadline(time.Time{})

	go conn.worker()

	return
}

func newConn(scheme, addr string, opts Options) (conn *Connection, err error) {

	defer func() { // close opened connection if error
		if err != nil && conn != nil {
			if conn.tcpConn != nil {
				conn.tcpConn.Close()
			}
			conn = nil
		}
	}()

	conn = &Connection{
		remoteAddr:        addr,
		requests:          newRequestMap(),
		writeChan:         make(chan *request, 256),
		exit:              make(chan bool),
		closed:            make(chan bool),
		firstErrorLock:    &sync.Mutex{},
		packData:          newPackData(opts.DefaultSpace),
		queryTimeout:      opts.QueryTimeout,
		perf:              opts.Perf,
		poolMaxPacketSize: opts.PoolMaxPacketSize,
	}

	conn.tcpConn, err = net.DialTimeout(scheme, conn.remoteAddr, opts.ConnectTimeout)
	if err != nil {
		return nil, err
	}

	if conn.perf.NetRead != nil {
		conn.ccr = NewCountedReader(conn.tcpConn, conn.perf.NetRead)
	} else {
		conn.ccr = conn.tcpConn
	}

	if conn.perf.NetWrite != nil {
		conn.ccw = NewCountedWriter(conn.tcpConn, conn.perf.NetWrite)
	} else {
		conn.ccw = conn.tcpConn
	}

	greeting := make([]byte, 128)

	connectDeadline := time.Now().Add(opts.ConnectTimeout)
	conn.tcpConn.SetDeadline(connectDeadline)
	// removing deadline deferred
	defer conn.tcpConn.SetDeadline(time.Time{})

	_, err = io.ReadFull(conn.ccr, greeting)
	if err != nil {
		return
	}

	conn.greeting = &Greeting{
		Version: greeting[:64],
		Auth:    greeting[64:108],
	}

	// try to authenticate if user have been provided
	if len(opts.User) > 0 {
		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)

		err = pp.packMsg(&Auth{
			User:         opts.User,
			Password:     opts.Password,
			GreetingAuth: conn.greeting.Auth,
		}, conn.packData)
		if err != nil {
			conn.releasePacket(pp)
			return
		}

		_, err = pp.WriteTo(conn.ccw)
		conn.releasePacket(pp)
		if err != nil {
			return
		}

		pp = packetPool.Get()
		defer conn.releasePacket(pp)

		if err = pp.readPacket(conn.ccr); err != nil {
			return
		}

		authResponse := &pp.packet
		if authResponse.requestID != requestID {
			err = ErrSyncFailed
			return
		}

		if authResponse.Result != nil && authResponse.Result.Error != nil {
			err = authResponse.Result.Error
			return
		}
	}

	return
}

func parseOptions(dsnString string, opts Options) (*url.URL, Options, error) {
	// remove schema, if present
	// === for backward compatibility (only tcp despite of user wishes :)
	dsnString = strings.TrimPrefix(dsnString, "unix:")
	// ===

	// tcp is the default scheme
	switch {
	case strings.HasPrefix(dsnString, "tcp://"):
	case strings.HasPrefix(dsnString, "//"):
		dsnString = "tcp:" + dsnString
	default:
		dsnString = "tcp://" + dsnString
	}
	dsn, err := url.Parse(dsnString)
	if err != nil {
		return dsn, opts, err
	}

	if len(opts.User) == 0 {
		if user := dsn.User; user != nil {
			opts.User = user.Username()
			opts.Password, _ = user.Password()
		}
	}

	if len(opts.DefaultSpace) == 0 && len(dsn.Path) > 0 {
		path := strings.TrimPrefix(dsn.Path, "/")
		// check it if it is necessary
		switch {
		case len(path) == 0:
			return nil, opts, ErrEmptyDefaultSpace
		//case strings.IndexAny(path, "/ ,") != -1:
		//	return nil, opts, ErrBadDSNPath
		default:
			opts.DefaultSpace = path
		}
	}

	if opts.ConnectTimeout.Nanoseconds() == 0 {
		opts.ConnectTimeout = DefaultConnectTimeout
	}
	if opts.QueryTimeout.Nanoseconds() == 0 {
		opts.QueryTimeout = DefaultQueryTimeout
	}

	return dsn, opts, nil
}

func (conn *Connection) pullSchema() (err error) {
	// select space and index schema
	request := func(q Query) (*Result, error) {
		var err error

		requestID := conn.nextID()

		pp := packetPool.GetWithID(requestID)
		if err = pp.packMsg(q, conn.packData); err != nil {
			conn.releasePacket(pp)
			return nil, err
		}

		_, err = pp.WriteTo(conn.ccw)
		conn.releasePacket(pp)
		if err != nil {
			return nil, err
		}

		pp = packetPool.Get()
		defer conn.releasePacket(pp)

		if err = pp.readPacket(conn.ccr); err != nil {
			return nil, err
		}

		response := &pp.packet
		if response.requestID != requestID {
			return nil, errors.New("Bad response requestID")
		}

		if response.Result == nil {
			return nil, errors.New("Nil response result")
		}

		if response.Result.Error != nil {
			return nil, response.Result.Error
		}

		return response.Result, nil
	}

	res, err := request(&Select{
		Space:    ViewSpace,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, space := range res.Data {
		spaceID, _ := conn.packData.spaceNo(space[0])
		conn.packData.spaceMap[space[2].(string)] = spaceID
	}

	res, err = request(&Select{
		Space:    ViewIndex,
		Key:      0,
		Iterator: IterAll,
	})
	if err != nil {
		return
	}

	for _, index := range res.Data {
		spaceID, _ := conn.packData.fieldNo(index[0])
		indexID, _ := conn.packData.fieldNo(index[1])
		indexName := index[2].(string)
		indexAttr := index[4].(map[string]interface{}) // e.g: {"unique": true}
		indexFields := index[5].([]interface{})        // e.g: [[0 num] [1 str]]

		indexSpaceMap, exists := conn.packData.indexMap[spaceID]
		if !exists {
			indexSpaceMap = make(map[string]uint64)
			conn.packData.indexMap[spaceID] = indexSpaceMap
		}
		indexSpaceMap[indexName] = indexID

		// build list of primary key field numbers for this space, if the PK is detected
		if indexAttr != nil && indexID == 0 {
			if unique, ok := indexAttr["unique"]; ok && unique.(bool) {
				pk := make([]int, len(indexFields))
				for i := range indexFields {
					descr := indexFields[i].([]interface{})
					f, _ := conn.packData.fieldNo(descr[0])
					pk[i] = int(f)
				}
				conn.packData.primaryKeyMap[spaceID] = pk
			}
		}
	}

	return
}

func (conn *Connection) nextID() uint64 {
	return atomic.AddUint64(&conn.requestID, 1)
}

func (conn *Connection) stop() {
	conn.closeOnce.Do(func() {
		// debug.PrintStack()
		close(conn.exit)
		conn.tcpConn.Close()
		runtime.GC()
	})
}

func (conn *Connection) GetPerf() PerfCount {
	return conn.perf
}

func (conn *Connection) GetPrimaryKeyFields(space interface{}) ([]int, bool) {
	var spaceID uint64
	var err error

	if conn.packData == nil {
		return nil, false
	}
	if spaceID, err = conn.packData.spaceNo(space); err != nil {
		return nil, false
	}

	f, ok := conn.packData.primaryKeyMap[spaceID]
	return f, ok
}

func (conn *Connection) Close() {
	conn.stop()
	<-conn.closed
}

func (conn *Connection) String() string {
	return conn.remoteAddr
}

func (conn *Connection) IsClosed() bool {
	select {
	case <-conn.exit:
		return true
	default:
		return false
	}
}

func (conn *Connection) releasePacket(pp *BinaryPacket) {
	if conn.poolMaxPacketSize == 0 || conn.poolMaxPacketSize < cap(pp.body) {
		pp.Release()
	}
}

func (conn *Connection) getError() error {
	conn.firstErrorLock.Lock()
	defer conn.firstErrorLock.Unlock()
	return conn.firstError
}

func (conn *Connection) setError(err error) {
	if err != nil && err != io.EOF {
		conn.firstErrorLock.Lock()
		if conn.firstError == nil {
			conn.firstError = err
		}
		conn.firstErrorLock.Unlock()
	}
}

func (conn *Connection) worker() {
	var wg sync.WaitGroup

	wg.Add(2)

	go func() {
		err := conn.writer()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	go func() {
		err := conn.reader()
		conn.setError(err)
		conn.stop()
		wg.Done()
	}()

	wg.Wait()

	// release all pending packets
	writeChan := conn.writeChan

CLEANUP_LOOP:
	for {
		select {
		case req := <-writeChan:
			pp := req.packet
			if pp != nil {
				req.packet = nil
				conn.releasePacket(pp)
			}
		default:
			break CLEANUP_LOOP
		}
	}

	// send error reply to all pending requests
	conn.requests.CleanUp(func(req *request) {
		select {
		case req.replyChan <- &AsyncResult{
			Error:     ConnectionClosedError(conn),
			ErrorCode: ErrNoConnection,
			Opaque:    req.opaque,
		}:
		default:
		}
		requestPool.Put(req)
	})

	close(conn.closed)
}

func (conn *Connection) writer() (err error) {
	writeChan := conn.writeChan
	stopChan := conn.exit
	w := bufio.NewWriterSize(conn.ccw, DefaultWriterBufSize)

	wr := func(w io.Writer, req *request) error {
		packet := req.packet

		if conn.perf.NetPacketsOut != nil {
			conn.perf.NetPacketsOut.Add(1)
		}
		if conn.perf.QueryComplete != nil && req.opaque != nil {
			req.startedAt = time.Now()
		}

		_, err := packet.WriteTo(w)
		req.packet = nil
		conn.releasePacket(packet)
		return err
	}

WRITER_LOOP:
	for {
		select {
		case req, ok := <-writeChan:
			if !ok {
				break WRITER_LOOP
			}
			if err = wr(w, req); err != nil {
				break WRITER_LOOP
			}
		case <-stopChan:
			break WRITER_LOOP
		default:
			if err = w.Flush(); err != nil {
				break WRITER_LOOP
			}

			// same without flush
			select {
			case req, ok := <-writeChan:
				if !ok {
					break WRITER_LOOP
				}
				if err = wr(w, req); err != nil {
					break WRITER_LOOP
				}
			case <-stopChan:
				break WRITER_LOOP
			}
		}
	}

	return
}

func (conn *Connection) reader() (err error) {
	var pp *BinaryPacket
	var requestID uint64

	r := bufio.NewReaderSize(conn.ccr, DefaultReaderBufSize)

READER_LOOP:
	for {
		pp := packetPool.Get()
		if requestID, err = pp.readRawPacket(r); err != nil {
			break READER_LOOP
		}

		if conn.perf.NetPacketsIn != nil {
			conn.perf.NetPacketsIn.Add(1)
		}

		req := conn.requests.Pop(requestID)
		if req == nil {
			conn.releasePacket(pp)
			pp = nil
			continue
		}

		if conn.perf.QueryComplete != nil && req.opaque != nil {
			conn.perf.QueryComplete(req.opaque, time.Since(req.startedAt))
		}

		select {
		case req.replyChan <- &AsyncResult{0, nil, pp, conn, req.opaque}:
			pp = nil
		default:
		}

		requestPool.Put(req)
	}

	if pp != nil {
		conn.releasePacket(pp)
	}
	return
}
<|endoftext|>


<commit_before>package blocks

import (
	"container/heap"
	"log"
	"time"
)

func Histogram(b *Block) {

	type histogramRule struct {
		Window int
		Key    string
	}

	type histogramBucket struct {
		Count int
		Label string
	}

	type histogramData struct {
		Histogram []histogramBucket
	}

	data := &histogramData{}

	var rule *histogramRule

	waitTimer := time.NewTimer(100 * time.Millisecond)
	window := time.Duration(0)

	histogram := map[string]*PriorityQueue{}
	emptyByte := make([]byte, 0)

	for {
		select {
		case query := <-b.Routes["histogram"]:
			data.Histogram = make([]histogramBucket, len(histogram))
			i := 0
			for k, pq := range histogram {
				bucket := histogramBucket{
					Count: len(*pq),
					Label: k,
				}
				data.Histogram[i] = bucket
				i++
			}
			marshal(query, data)
		case msg := <-b.Routes["get_rule"]:
			if rule == nil {
				marshal(msg, &histogramRule{})
			} else {
				marshal(msg, rule)
			}
		case ruleUpdate := <-b.Routes["set_rule"]:
			if rule == nil {
				rule = &histogramRule{}
			}

			unmarshal(ruleUpdate, rule)
			window = time.Duration(rule.Window) * time.Second
		case msg := <-b.InChan:
			if rule == nil {
				break
			}

			value := getKeyValues(msg, rule.Key)[0]
			valueString, ok := value.(string)
			if !ok {
				log.Println("nil value against", rule.Key, " - ignoring")
				break
			}

			if pq, ok := histogram[valueString]; ok {
				queueMessage := &PQMessage{
					val: &emptyByte,
					t:   time.Now(),
				}
				heap.Push(pq, queueMessage)
			} else {
				pq := &PriorityQueue{}
				heap.Init(pq)
				histogram[valueString] = pq
				queueMessage := &PQMessage{
					val: &emptyByte,
					t:   time.Now(),
				}
				heap.Push(pq, queueMessage)
			}
		case <-waitTimer.C:
		}
		for _, pq := range histogram {
			for {
				pqMsg, diff := pq.PeekAndShift(time.Now(), window)
				if pqMsg == nil {
					// either the queue is empty, or it's not time to emit
					if diff == 0 {
						// then the queue is empty. Pause for 5 seconds before checking again
						diff = time.Duration(500) * time.Millisecond
					}
					waitTimer.Reset(diff)
					break
				}
			}
		}
	}
}
<commit_msg>fixed histogram quit chan<commit_after>package blocks

import (
	"container/heap"
	"log"
	"time"
)

func Histogram(b *Block) {

	type histogramRule struct {
		Window int
		Key    string
	}

	type histogramBucket struct {
		Count int
		Label string
	}

	type histogramData struct {
		Histogram []histogramBucket
	}

	data := &histogramData{}

	var rule *histogramRule

	waitTimer := time.NewTimer(100 * time.Millisecond)
	window := time.Duration(0)

	histogram := map[string]*PriorityQueue{}
	emptyByte := make([]byte, 0)

	for {
		select {
		case query := <-b.Routes["histogram"]:
			data.Histogram = make([]histogramBucket, len(histogram))
			i := 0
			for k, pq := range histogram {
				bucket := histogramBucket{
					Count: len(*pq),
					Label: k,
				}
				data.Histogram[i] = bucket
				i++
			}
			marshal(query, data)
		case msg := <-b.Routes["get_rule"]:
			if rule == nil {
				marshal(msg, &histogramRule{})
			} else {
				marshal(msg, rule)
			}
		case ruleUpdate := <-b.Routes["set_rule"]:
			if rule == nil {
				rule = &histogramRule{}
			}

			unmarshal(ruleUpdate, rule)
			window = time.Duration(rule.Window) * time.Second
		case msg := <-b.InChan:
			if rule == nil {
				break
			}

			value := getKeyValues(msg, rule.Key)[0]
			valueString, ok := value.(string)
			if !ok {
				log.Println("nil value against", rule.Key, " - ignoring")
				break
			}

			if pq, ok := histogram[valueString]; ok {
				queueMessage := &PQMessage{
					val: &emptyByte,
					t:   time.Now(),
				}
				heap.Push(pq, queueMessage)
			} else {
				pq := &PriorityQueue{}
				heap.Init(pq)
				histogram[valueString] = pq
				queueMessage := &PQMessage{
					val: &emptyByte,
					t:   time.Now(),
				}
				heap.Push(pq, queueMessage)
			}
		case <-waitTimer.C:
		case <-b.QuitChan:
			quit(b)
			return
		}
		for _, pq := range histogram {
			for {
				pqMsg, diff := pq.PeekAndShift(time.Now(), window)
				if pqMsg == nil {
					// either the queue is empty, or it's not time to emit
					if diff == 0 {
						// then the queue is empty. Pause for 5 seconds before checking again
						diff = time.Duration(500) * time.Millisecond
					}
					waitTimer.Reset(diff)
					break
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package memstats

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/gbbr/memstats/internal/web"
	"golang.org/x/net/websocket"
)

type server struct {
	ListenAddr string
	Tick       time.Duration
}

func Serve(opts ...func(*server)) {
	var s server
	defaults(&s)

	for _, fn := range opts {
		fn(&s)
	}
	ln, err := net.Listen("tcp", s.ListenAddr)
	if err != nil {
		log.Fatalf("memstat: %s", err)
	}
	s.ListenAddr = ln.Addr().String()

	mux := http.NewServeMux()
	mux.Handle("/", s)
	mux.Handle("/memstats-feed", websocket.Handler(s.ServeSocket))
	if err = http.Serve(ln, mux); err != nil {
		log.Fatalf("memstat: %s", err)
	}
}

func (s server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t, err := web.Template()
	if err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
		return
	}
	if err := t.ExecuteTemplate(w, "main", s); err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
	}
}

func (s server) ServeSocket(ws *websocket.Conn) {
	payload := struct {
		Stats runtime.MemStats
	}{}
	for {
		runtime.ReadMemStats(&payload.Stats)
		websocket.JSON.Send(ws, payload)
		<-time.After(s.Tick)
	}
	pprof.StopCPUProfile()
}

func defaults(s *server) {
	s.ListenAddr = ":6061"
	s.Tick = 2 * time.Second
}
<commit_msg>break disconnected loops<commit_after>package memstats

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/gbbr/memstats/internal/web"
	"golang.org/x/net/websocket"
)

type server struct {
	ListenAddr string
	Tick       time.Duration
}

func Serve(opts ...func(*server)) {
	var s server
	defaults(&s)

	for _, fn := range opts {
		fn(&s)
	}
	ln, err := net.Listen("tcp", s.ListenAddr)
	if err != nil {
		log.Fatalf("memstat: %s", err)
	}
	s.ListenAddr = ln.Addr().String()

	mux := http.NewServeMux()
	mux.Handle("/", s)
	mux.Handle("/memstats-feed", websocket.Handler(s.ServeMemStats))
	if err = http.Serve(ln, mux); err != nil {
		log.Fatalf("memstat: %s", err)
	}
}

func (s server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t, err := web.Template()
	if err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
		return
	}
	if err := t.ExecuteTemplate(w, "main", s); err != nil {
		fmt.Fprintf(w, "Error parsing template: %s", err)
	}
}

func (s server) ServeMemStats(ws *websocket.Conn) {
	payload := struct {
		Stats runtime.MemStats
	}{}
	for {
		runtime.ReadMemStats(&payload.Stats)
		err := websocket.JSON.Send(ws, payload)
		if err != nil {
			break
		}
		<-time.After(s.Tick)
	}
	pprof.StopCPUProfile()
	ws.Close()
}

func defaults(s *server) {
	s.ListenAddr = ":6061"
	s.Tick = 2 * time.Second
}
<|endoftext|>


<commit_before>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// 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 casbin

import (
	"errors"
	"fmt"

	"github.com/Knetic/govaluate"
	"github.com/casbin/casbin/effect"
	"github.com/casbin/casbin/model"
	"github.com/casbin/casbin/persist"
	"github.com/casbin/casbin/persist/file-adapter"
	"github.com/casbin/casbin/rbac"
	"github.com/casbin/casbin/rbac/default-role-manager"
	"github.com/casbin/casbin/util"
)

// Enforcer is the main interface for authorization enforcement and policy management.
type Enforcer struct {
	modelPath string
	model     model.Model
	fm        model.FunctionMap
	eft       effect.Effector

	adapter persist.Adapter
	watcher persist.Watcher
	rm      rbac.RoleManager

	enabled            bool
	autoSave           bool
	autoBuildRoleLinks bool
}

// NewEnforcer creates an enforcer via file or DB.
// File:
// e := casbin.NewEnforcer("path/to/basic_model.conf", "path/to/basic_policy.conf")
// MySQL DB:
// a := mysqladapter.NewDBAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/")
// e := casbin.NewEnforcer("path/to/basic_model.conf", a)
func NewEnforcer(params ...interface{}) *Enforcer {
	e := &Enforcer{}
	e.rm = defaultrolemanager.NewRoleManager(10)
	e.eft = effect.NewDefaultEffector()

	parsedParamLen := 0
	if len(params) >= 1 {
		enableLog, ok := params[len(params)-1].(bool)
		if ok {
			e.EnableLog(enableLog)

			parsedParamLen++
		}
	}

	if len(params)-parsedParamLen == 2 {
		switch params[0].(type) {
		case string:
			switch params[1].(type) {
			case string:
				e.InitWithFile(params[0].(string), params[1].(string))
			default:
				e.InitWithAdapter(params[0].(string), params[1].(persist.Adapter))
			}
		default:
			switch params[1].(type) {
			case string:
				panic("Invalid parameters for enforcer.")
			default:
				e.InitWithModelAndAdapter(params[0].(model.Model), params[1].(persist.Adapter))
			}
		}
	} else if len(params)-parsedParamLen == 1 {
		switch params[0].(type) {
		case string:
			e.InitWithFile(params[0].(string), "")
		default:
			e.InitWithModelAndAdapter(params[0].(model.Model), nil)
		}
	} else if len(params)-parsedParamLen == 0 {
		e.InitWithFile("", "")
	} else {
		panic("Invalid parameters for enforcer.")
	}

	return e
}

// InitWithFile initializes an enforcer with a model file and a policy file.
func (e *Enforcer) InitWithFile(modelPath string, policyPath string) {
	a := fileadapter.NewAdapter(policyPath)
	e.InitWithAdapter(modelPath, a)
}

// InitWithAdapter initializes an enforcer with a database adapter.
func (e *Enforcer) InitWithAdapter(modelPath string, adapter persist.Adapter) {
	if modelPath == "" {
		return
	}
	e.modelPath = modelPath

	m := NewModel()
	m.LoadModel(modelPath)
	e.InitWithModelAndAdapter(m, adapter)
}

// InitWithModelAndAdapter initializes an enforcer with a model and a database adapter.
func (e *Enforcer) InitWithModelAndAdapter(m model.Model, adapter persist.Adapter) {
	e.adapter = adapter
	e.watcher = nil

	e.model = m
	e.model.PrintModel()
	e.fm = model.LoadFunctionMap()

	e.initialize()

	if e.adapter != nil {
		e.LoadPolicy()
	}
}

func (e *Enforcer) initialize() {
	e.enabled = true
	e.autoSave = true
	e.autoBuildRoleLinks = true
}

// NewModel creates a model.
func NewModel(text ...string) model.Model {
	m := make(model.Model)

	if len(text) == 1 {
		m.LoadModelFromText(text[0])
	} else if len(text) != 0 {
		panic("Invalid parameters for model.")
	}

	return m
}

// LoadModel reloads the model from the model CONF file.
// Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy().
func (e *Enforcer) LoadModel() {
	e.model = NewModel()
	e.model.LoadModel(e.modelPath)
	e.model.PrintModel()
	e.fm = model.LoadFunctionMap()
}

// GetModel gets the current model.
func (e *Enforcer) GetModel() model.Model {
	return e.model
}

// SetModel sets the current model.
func (e *Enforcer) SetModel(m model.Model) {
	e.model = m
	e.fm = model.LoadFunctionMap()
}

// GetAdapter gets the current adapter.
func (e *Enforcer) GetAdapter() persist.Adapter {
	return e.adapter
}

// SetAdapter sets the current adapter.
func (e *Enforcer) SetAdapter(adapter persist.Adapter) {
	e.adapter = adapter
}

// SetWatcher sets the current watcher.
func (e *Enforcer) SetWatcher(watcher persist.Watcher) {
	e.watcher = watcher
	watcher.SetUpdateCallback(func(string) { e.LoadPolicy() })
}

// SetRoleManager sets the current role manager.
func (e *Enforcer) SetRoleManager(rm rbac.RoleManager) {
	e.rm = rm
}

// SetEffector sets the current effector.
func (e *Enforcer) SetEffector(eft effect.Effector) {
	e.eft = eft
}

// ClearPolicy clears all policy.
func (e *Enforcer) ClearPolicy() {
	e.model.ClearPolicy()
}

// LoadPolicy reloads the policy from file/database.
func (e *Enforcer) LoadPolicy() error {
	e.model.ClearPolicy()
	err := e.adapter.LoadPolicy(e.model)
	if err != nil {
		return err
	}

	e.model.PrintPolicy()
	if e.autoBuildRoleLinks {
		e.BuildRoleLinks()
	}
	return nil
}

// LoadFilteredPolicy reloads a filtered policy from file/database.
func (e *Enforcer) LoadFilteredPolicy(filter interface{}) error {
	e.model.ClearPolicy()

	var filteredAdapter persist.FilteredAdapter

	// Attempt to cast the Adapter as a FilteredAdapter
	switch e.adapter.(type) {
	case persist.FilteredAdapter:
		filteredAdapter = e.adapter.(persist.FilteredAdapter)
	default:
		return errors.New("filtered policies are not supported by this adapter")
	}
	err := filteredAdapter.LoadFilteredPolicy(e.model, filter)
	if err != nil {
		return err
	}

	e.model.PrintPolicy()
	if e.autoBuildRoleLinks {
		e.BuildRoleLinks()
	}
	return nil
}

// IsFiltered returns true if the loaded policy has been filtered.
func (e *Enforcer) IsFiltered() bool {
	filteredAdapter, ok := e.adapter.(persist.FilteredAdapter)
	if !ok {
		return false
	}
	return filteredAdapter.IsFiltered()
}

// SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database.
func (e *Enforcer) SavePolicy() error {
	if e.IsFiltered() {
		return errors.New("cannot save a filtered policy")
	}
	err := e.adapter.SavePolicy(e.model)
	if err == nil {
		if e.watcher != nil {
			e.watcher.Update()
		}
	}
	return err
}

// EnableEnforce changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function.
func (e *Enforcer) EnableEnforce(enable bool) {
	e.enabled = enable
}

// EnableLog changes whether to print Casbin log to the standard output.
func (e *Enforcer) EnableLog(enable bool) {
	util.EnableLog = enable
}

// EnableAutoSave controls whether to save a policy rule automatically to the adapter when it is added or removed.
func (e *Enforcer) EnableAutoSave(autoSave bool) {
	e.autoSave = autoSave
}

// EnableAutoBuildRoleLinks controls whether to rebuild the role inheritance relations when a role is added or deleted.
func (e *Enforcer) EnableAutoBuildRoleLinks(autoBuildRoleLinks bool) {
	e.autoBuildRoleLinks = autoBuildRoleLinks
}

// BuildRoleLinks manually rebuild the role inheritance relations.
func (e *Enforcer) BuildRoleLinks() {
	e.rm.Clear()
	e.model.BuildRoleLinks(e.rm)
}

func (e *Enforcer) generateGFunction(rm rbac.RoleManager) func(args ...interface{}) (interface{}, error) {
	return func(args ...interface{}) (interface{}, error) {
		if rm == nil {
			name1 := args[0].(string)
			name2 := args[1].(string)

			return name1 == name2, nil
		}

		if len(args) == 2 {
			name1 := args[0].(string)
			name2 := args[1].(string)

			res, _ := rm.HasLink(name1, name2)
			return res, nil
		}

		name1 := args[0].(string)
		name2 := args[1].(string)
		domain := args[2].(string)

		res, _ := rm.HasLink(name1, name2, domain)
		return res, nil
	}
}

// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
func (e *Enforcer) Enforce(rvals ...interface{}) bool {
	if !e.enabled {
		return true
	}

	functions := make(map[string]govaluate.ExpressionFunction)
	for key, function := range e.fm {
		functions[key] = function
	}
	if _, ok := e.model["g"]; ok {
		for key, ast := range e.model["g"] {
			rm := ast.RM
			functions[key] = e.generateGFunction(rm)
		}
	}

	expString := e.model["m"]["m"].Value
	expression, _ := govaluate.NewEvaluableExpressionWithFunctions(expString, functions)

	var policyEffects []effect.Effect
	var matcherResults []float64
	if policyLen := len(e.model["p"]["p"].Policy); policyLen != 0 {
		policyEffects = make([]effect.Effect, policyLen)
		matcherResults = make([]float64, policyLen)

		for i, pvals := range e.model["p"]["p"].Policy {
			// util.LogPrint("Policy Rule: ", pvals)

			parameters := make(map[string]interface{}, 8)
			for j, token := range e.model["r"]["r"].Tokens {
				parameters[token] = rvals[j]
			}
			for j, token := range e.model["p"]["p"].Tokens {
				parameters[token] = pvals[j]
			}

			result, err := expression.Evaluate(parameters)
			// util.LogPrint("Result: ", result)

			if err != nil {
				policyEffects[i] = effect.Indeterminate
				panic(err)
			} else {
				switch result.(type) {
				case bool:
					if !result.(bool) {
						policyEffects[i] = effect.Indeterminate
						continue
					}
				case float64:
					if result.(float64) == 0 {
						policyEffects[i] = effect.Indeterminate
						continue
					} else {
						matcherResults[i] = result.(float64)
					}
				default:
					panic(errors.New("matcher result should be bool, int or float"))
				}
				if eft, ok := parameters["p_eft"]; ok {
					if eft == "allow" {
						policyEffects[i] = effect.Allow
					} else if eft == "deny" {
						policyEffects[i] = effect.Deny
					} else {
						policyEffects[i] = effect.Indeterminate
					}
				} else {
					policyEffects[i] = effect.Allow
				}

				if e.model["e"]["e"].Value == "priority(p_eft) || deny" {
					break
				}
			}
		}
	} else {
		policyEffects = make([]effect.Effect, 1)
		matcherResults = make([]float64, 1)

		parameters := make(map[string]interface{}, 8)
		for j, token := range e.model["r"]["r"].Tokens {
			parameters[token] = rvals[j]
		}
		for _, token := range e.model["p"]["p"].Tokens {
			parameters[token] = ""
		}

		result, err := expression.Evaluate(parameters)
		// util.LogPrint("Result: ", result)

		if err != nil {
			policyEffects[0] = effect.Indeterminate
			panic(err)
		} else {
			if result.(bool) {
				policyEffects[0] = effect.Allow
			} else {
				policyEffects[0] = effect.Indeterminate
			}
		}
	}

	// util.LogPrint("Rule Results: ", policyEffects)

	result, err := e.eft.MergeEffects(e.model["e"]["e"].Value, policyEffects, matcherResults)
	if err != nil {
		panic(err)
	}

	reqStr := "Request: "
	for i, rval := range rvals {
		if i != len(rvals)-1 {
			reqStr += fmt.Sprintf("%v, ", rval)
		} else {
			reqStr += fmt.Sprintf("%v", rval)
		}
	}
	reqStr += fmt.Sprintf(" ---> %t", result)
	util.LogPrint(reqStr)

	return result
}
<commit_msg>Accept modelPath in NewModel().<commit_after>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// 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 casbin

import (
	"errors"
	"fmt"

	"github.com/Knetic/govaluate"
	"github.com/casbin/casbin/effect"
	"github.com/casbin/casbin/model"
	"github.com/casbin/casbin/persist"
	"github.com/casbin/casbin/persist/file-adapter"
	"github.com/casbin/casbin/rbac"
	"github.com/casbin/casbin/rbac/default-role-manager"
	"github.com/casbin/casbin/util"
)

// Enforcer is the main interface for authorization enforcement and policy management.
type Enforcer struct {
	modelPath string
	model     model.Model
	fm        model.FunctionMap
	eft       effect.Effector

	adapter persist.Adapter
	watcher persist.Watcher
	rm      rbac.RoleManager

	enabled            bool
	autoSave           bool
	autoBuildRoleLinks bool
}

// NewEnforcer creates an enforcer via file or DB.
// File:
// e := casbin.NewEnforcer("path/to/basic_model.conf", "path/to/basic_policy.conf")
// MySQL DB:
// a := mysqladapter.NewDBAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/")
// e := casbin.NewEnforcer("path/to/basic_model.conf", a)
func NewEnforcer(params ...interface{}) *Enforcer {
	e := &Enforcer{}
	e.rm = defaultrolemanager.NewRoleManager(10)
	e.eft = effect.NewDefaultEffector()

	parsedParamLen := 0
	if len(params) >= 1 {
		enableLog, ok := params[len(params)-1].(bool)
		if ok {
			e.EnableLog(enableLog)

			parsedParamLen++
		}
	}

	if len(params)-parsedParamLen == 2 {
		switch params[0].(type) {
		case string:
			switch params[1].(type) {
			case string:
				e.InitWithFile(params[0].(string), params[1].(string))
			default:
				e.InitWithAdapter(params[0].(string), params[1].(persist.Adapter))
			}
		default:
			switch params[1].(type) {
			case string:
				panic("Invalid parameters for enforcer.")
			default:
				e.InitWithModelAndAdapter(params[0].(model.Model), params[1].(persist.Adapter))
			}
		}
	} else if len(params)-parsedParamLen == 1 {
		switch params[0].(type) {
		case string:
			e.InitWithFile(params[0].(string), "")
		default:
			e.InitWithModelAndAdapter(params[0].(model.Model), nil)
		}
	} else if len(params)-parsedParamLen == 0 {
		e.InitWithFile("", "")
	} else {
		panic("Invalid parameters for enforcer.")
	}

	return e
}

// InitWithFile initializes an enforcer with a model file and a policy file.
func (e *Enforcer) InitWithFile(modelPath string, policyPath string) {
	a := fileadapter.NewAdapter(policyPath)
	e.InitWithAdapter(modelPath, a)
}

// InitWithAdapter initializes an enforcer with a database adapter.
func (e *Enforcer) InitWithAdapter(modelPath string, adapter persist.Adapter) {
	m := NewModel(modelPath, "")
	e.InitWithModelAndAdapter(m, adapter)

	e.modelPath = modelPath
}

// InitWithModelAndAdapter initializes an enforcer with a model and a database adapter.
func (e *Enforcer) InitWithModelAndAdapter(m model.Model, adapter persist.Adapter) {
	e.adapter = adapter
	e.watcher = nil

	e.model = m
	e.model.PrintModel()
	e.fm = model.LoadFunctionMap()

	e.initialize()

	if e.adapter != nil {
		e.LoadPolicy()
	}
}

func (e *Enforcer) initialize() {
	e.enabled = true
	e.autoSave = true
	e.autoBuildRoleLinks = true
}

// NewModel creates a model.
func NewModel(text ...string) model.Model {
	m := make(model.Model)

	if len(text) == 2 {
		if text[0] != "" {
			m.LoadModel(text[0])
		}
	} else if len(text) == 1 {
		m.LoadModelFromText(text[0])
	} else if len(text) != 0 {
		panic("Invalid parameters for model.")
	}

	return m
}

// LoadModel reloads the model from the model CONF file.
// Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy().
func (e *Enforcer) LoadModel() {
	e.model = NewModel()
	e.model.LoadModel(e.modelPath)
	e.model.PrintModel()
	e.fm = model.LoadFunctionMap()
}

// GetModel gets the current model.
func (e *Enforcer) GetModel() model.Model {
	return e.model
}

// SetModel sets the current model.
func (e *Enforcer) SetModel(m model.Model) {
	e.model = m
	e.fm = model.LoadFunctionMap()
}

// GetAdapter gets the current adapter.
func (e *Enforcer) GetAdapter() persist.Adapter {
	return e.adapter
}

// SetAdapter sets the current adapter.
func (e *Enforcer) SetAdapter(adapter persist.Adapter) {
	e.adapter = adapter
}

// SetWatcher sets the current watcher.
func (e *Enforcer) SetWatcher(watcher persist.Watcher) {
	e.watcher = watcher
	watcher.SetUpdateCallback(func(string) { e.LoadPolicy() })
}

// SetRoleManager sets the current role manager.
func (e *Enforcer) SetRoleManager(rm rbac.RoleManager) {
	e.rm = rm
}

// SetEffector sets the current effector.
func (e *Enforcer) SetEffector(eft effect.Effector) {
	e.eft = eft
}

// ClearPolicy clears all policy.
func (e *Enforcer) ClearPolicy() {
	e.model.ClearPolicy()
}

// LoadPolicy reloads the policy from file/database.
func (e *Enforcer) LoadPolicy() error {
	e.model.ClearPolicy()
	err := e.adapter.LoadPolicy(e.model)
	if err != nil {
		return err
	}

	e.model.PrintPolicy()
	if e.autoBuildRoleLinks {
		e.BuildRoleLinks()
	}
	return nil
}

// LoadFilteredPolicy reloads a filtered policy from file/database.
func (e *Enforcer) LoadFilteredPolicy(filter interface{}) error {
	e.model.ClearPolicy()

	var filteredAdapter persist.FilteredAdapter

	// Attempt to cast the Adapter as a FilteredAdapter
	switch e.adapter.(type) {
	case persist.FilteredAdapter:
		filteredAdapter = e.adapter.(persist.FilteredAdapter)
	default:
		return errors.New("filtered policies are not supported by this adapter")
	}
	err := filteredAdapter.LoadFilteredPolicy(e.model, filter)
	if err != nil {
		return err
	}

	e.model.PrintPolicy()
	if e.autoBuildRoleLinks {
		e.BuildRoleLinks()
	}
	return nil
}

// IsFiltered returns true if the loaded policy has been filtered.
func (e *Enforcer) IsFiltered() bool {
	filteredAdapter, ok := e.adapter.(persist.FilteredAdapter)
	if !ok {
		return false
	}
	return filteredAdapter.IsFiltered()
}

// SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database.
func (e *Enforcer) SavePolicy() error {
	if e.IsFiltered() {
		return errors.New("cannot save a filtered policy")
	}
	err := e.adapter.SavePolicy(e.model)
	if err == nil {
		if e.watcher != nil {
			e.watcher.Update()
		}
	}
	return err
}

// EnableEnforce changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function.
func (e *Enforcer) EnableEnforce(enable bool) {
	e.enabled = enable
}

// EnableLog changes whether to print Casbin log to the standard output.
func (e *Enforcer) EnableLog(enable bool) {
	util.EnableLog = enable
}

// EnableAutoSave controls whether to save a policy rule automatically to the adapter when it is added or removed.
func (e *Enforcer) EnableAutoSave(autoSave bool) {
	e.autoSave = autoSave
}

// EnableAutoBuildRoleLinks controls whether to rebuild the role inheritance relations when a role is added or deleted.
func (e *Enforcer) EnableAutoBuildRoleLinks(autoBuildRoleLinks bool) {
	e.autoBuildRoleLinks = autoBuildRoleLinks
}

// BuildRoleLinks manually rebuild the role inheritance relations.
func (e *Enforcer) BuildRoleLinks() {
	e.rm.Clear()
	e.model.BuildRoleLinks(e.rm)
}

func (e *Enforcer) generateGFunction(rm rbac.RoleManager) func(args ...interface{}) (interface{}, error) {
	return func(args ...interface{}) (interface{}, error) {
		if rm == nil {
			name1 := args[0].(string)
			name2 := args[1].(string)

			return name1 == name2, nil
		}

		if len(args) == 2 {
			name1 := args[0].(string)
			name2 := args[1].(string)

			res, _ := rm.HasLink(name1, name2)
			return res, nil
		}

		name1 := args[0].(string)
		name2 := args[1].(string)
		domain := args[2].(string)

		res, _ := rm.HasLink(name1, name2, domain)
		return res, nil
	}
}

// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
func (e *Enforcer) Enforce(rvals ...interface{}) bool {
	if !e.enabled {
		return true
	}

	functions := make(map[string]govaluate.ExpressionFunction)
	for key, function := range e.fm {
		functions[key] = function
	}
	if _, ok := e.model["g"]; ok {
		for key, ast := range e.model["g"] {
			rm := ast.RM
			functions[key] = e.generateGFunction(rm)
		}
	}

	expString := e.model["m"]["m"].Value
	expression, _ := govaluate.NewEvaluableExpressionWithFunctions(expString, functions)

	var policyEffects []effect.Effect
	var matcherResults []float64
	if policyLen := len(e.model["p"]["p"].Policy); policyLen != 0 {
		policyEffects = make([]effect.Effect, policyLen)
		matcherResults = make([]float64, policyLen)

		for i, pvals := range e.model["p"]["p"].Policy {
			// util.LogPrint("Policy Rule: ", pvals)

			parameters := make(map[string]interface{}, 8)
			for j, token := range e.model["r"]["r"].Tokens {
				parameters[token] = rvals[j]
			}
			for j, token := range e.model["p"]["p"].Tokens {
				parameters[token] = pvals[j]
			}

			result, err := expression.Evaluate(parameters)
			// util.LogPrint("Result: ", result)

			if err != nil {
				policyEffects[i] = effect.Indeterminate
				panic(err)
			} else {
				switch result.(type) {
				case bool:
					if !result.(bool) {
						policyEffects[i] = effect.Indeterminate
						continue
					}
				case float64:
					if result.(float64) == 0 {
						policyEffects[i] = effect.Indeterminate
						continue
					} else {
						matcherResults[i] = result.(float64)
					}
				default:
					panic(errors.New("matcher result should be bool, int or float"))
				}
				if eft, ok := parameters["p_eft"]; ok {
					if eft == "allow" {
						policyEffects[i] = effect.Allow
					} else if eft == "deny" {
						policyEffects[i] = effect.Deny
					} else {
						policyEffects[i] = effect.Indeterminate
					}
				} else {
					policyEffects[i] = effect.Allow
				}

				if e.model["e"]["e"].Value == "priority(p_eft) || deny" {
					break
				}
			}
		}
	} else {
		policyEffects = make([]effect.Effect, 1)
		matcherResults = make([]float64, 1)

		parameters := make(map[string]interface{}, 8)
		for j, token := range e.model["r"]["r"].Tokens {
			parameters[token] = rvals[j]
		}
		for _, token := range e.model["p"]["p"].Tokens {
			parameters[token] = ""
		}

		result, err := expression.Evaluate(parameters)
		// util.LogPrint("Result: ", result)

		if err != nil {
			policyEffects[0] = effect.Indeterminate
			panic(err)
		} else {
			if result.(bool) {
				policyEffects[0] = effect.Allow
			} else {
				policyEffects[0] = effect.Indeterminate
			}
		}
	}

	// util.LogPrint("Rule Results: ", policyEffects)

	result, err := e.eft.MergeEffects(e.model["e"]["e"].Value, policyEffects, matcherResults)
	if err != nil {
		panic(err)
	}

	reqStr := "Request: "
	for i, rval := range rvals {
		if i != len(rvals)-1 {
			reqStr += fmt.Sprintf("%v, ", rval)
		} else {
			reqStr += fmt.Sprintf("%v", rval)
		}
	}
	reqStr += fmt.Sprintf(" ---> %t", result)
	util.LogPrint(reqStr)

	return result
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		res := ""
		for _, _ = range currenttest.cs {
			res = res + "x\n"
		}
		return res, nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return currenttest.inspectVolumes(), nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		r := regexp.MustCompile(`.*\$([^#]+)###.*`)
		ss := r.FindStringSubmatch(cmd)
		if len(ss) == 2 {
			folder := ss[1]
			folder = folder + strings.Repeat("1", 64-len(folder))
			return folder, nil
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type contspecs []string
type Test struct {
	title string
	vs    volspecs
	cs    contspecs
	res   []int
	strs  []string
	ci    int
}

func newTest(title string) *Test {
	return &Test{title: title, res: []int{0, 0, 0, 0, 0}}
}
func (t *Test) setContainersPs(cs contspecs) *Test {
	t.cs = cs
	return t
}
func (t *Test) setVolumesLs(vs volspecs) *Test {
	t.vs = vs
	return t
}

type setterRes interface {
	setResAt(index int) *Test
}

type result struct {
	res int
	t   *Test
}

type resultOne struct {
	t *Test
}

func (t *Test) expects(number int) *result {
	return &result{t: t, res: number}
}

func (t *Test) expectOne() *resultOne {
	return &resultOne{t: t}
}

func (r *result) setResAt(index int) *Test {
	r.t.res[index] = r.res
	return r.t
}
func (r *resultOne) setResAt(index int) *Test {
	r.t.res[index] = 1
	return r.t
}

func (r *result) containers() *Test {
	return r.setResAt(0)
}
func (ro resultOne) container() *Test {
	return ro.setResAt(0)
}

func (r *result) volumes() *Test {
	return r.setResAt(2)
}
func (r *result) orphanedVolumes() *Test {
	return r.setResAt(3)
}

func (r *result) markers() *Test {
	return r.setResAt(4)
}

func (t *Test) mustProduce(strs []string) *Test {
	t.strs = strs
	return t
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		switch {
		case strings.HasSuffix(spec, "/"):
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		case strings.HasSuffix(spec, "@"):
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"

		default:
			res = res + spec + "\n"
		}
	}
	return res
}

func (t *Test) inspectVolumes() string {
	if len(t.cs) == 0 {
		return ""
	}
	res := t.cs[t.ci]
	t.ci = t.ci + 1
	return res
}

var deletions = []string{}
var tests = []*Test{
	newTest("empty vfs"),
	newTest("2 valid containers without any volume").
		setContainersPs([]string{"/contA,", "/contB,"}).
		expects(2).containers().
		mustProduce([]string{"cnt 'contA' (x)[false] - 0 vol", "cnt 'contB' (x)[false] - 0 vol"}),
	newTest("2 valid volumes").
		setVolumesLs([]string{"fa/", "fb/"}).
		expects(2).volumes().
		expects(2).orphanedVolumes().
		mustProduce([]string{"vol 'fa00000'<<nil>>", "vol 'fb11111'<<nil>>"}),
	newTest("Invalid (ill-formed) markers must be deleted").
		setVolumesLs([]string{"cainv/path/a@"}).
		expects(-1).markers(),
	/*
		Test{"Invalid (ill-formed) markers must be deleted", []string{"cainv/path/a@"}, []int{0, 0, 0, 0, -1}, []string{}},
		Test{"Invalid (no readlink) markers must be deleted", []string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"Invalid (no ls) markers must be deleted", []string{"ca;/path/nolsa@", "cb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"Invalid (no vdir) markers must be deleted", []string{"ca$novdira;/path/nolsa@", "cb$novdirb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"two valid markers", []string{"ca$fa;/path/vola@", "cb$fb;/path/volb@"}, []int{0, 0, 0, 0, 2}, []string{"marker 'fa11111'<ca$fa->/path/vola>", "marker 'fb11111'<cb$fb->/path/volb>"}},
		Test{"Invalid (bad name) volume", []string{"inva/"}, []int{0, 0, -1, 0, 0}, []string{}},
		Test{"Invalid file in volume vfs dir", []string{"invf"}, []int{0, 0, -1, 0, 0}, []string{}},
	*/
}
var currenttest *Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		fmt.Println("------ vvv " + test.title + " vvv ------")
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if nbvolumes(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], nbvolumes(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}

		for _, c := range tc {
			cs := c.String()
			check(cs, "container", test, t, i)
		}
		for _, v := range tv {
			vs := v.String()
			check(vs, "volume", test, t, i)
		}
		for _, m := range tm {
			ms := m.String()
			check(ms, "marker", test, t, i)
		}
		fmt.Println("------ ^^^ " + test.title + " ^^^ ------")
		fmt.Println("----------")
	}
}

func check(s string, tmsg string, test *Test, t *testing.T, i int) {
	found := false
	for _, tms := range test.strs {
		if s == tms {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("Test %d: '%s' expected %s >%s<, not found", i+1, test.title, tmsg, s)
	}

}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}

func nbvolumes(vm volumes) int {
	res := len(vm)
	for _, d := range deletions {
		if !strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<commit_msg>gcl_test.go: restore invalid markers tests<commit_after>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		res := ""
		for _, _ = range currenttest.cs {
			res = res + "x\n"
		}
		return res, nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return currenttest.inspectVolumes(), nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		r := regexp.MustCompile(`.*\$([^#]+)###.*`)
		ss := r.FindStringSubmatch(cmd)
		if len(ss) == 2 {
			folder := ss[1]
			folder = folder + strings.Repeat("1", 64-len(folder))
			return folder, nil
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type contspecs []string
type Test struct {
	title string
	vs    volspecs
	cs    contspecs
	res   []int
	strs  []string
	ci    int
}

func newTest(title string) *Test {
	return &Test{title: title, res: []int{0, 0, 0, 0, 0}}
}
func (t *Test) setContainersPs(cs contspecs) *Test {
	t.cs = cs
	return t
}
func (t *Test) setVolumesLs(vs volspecs) *Test {
	t.vs = vs
	return t
}

type setterRes interface {
	setResAt(index int) *Test
}

type result struct {
	res int
	t   *Test
}

type resultOne struct {
	t *Test
}

func (t *Test) expects(number int) *result {
	return &result{t: t, res: number}
}

func (t *Test) expectOne() *resultOne {
	return &resultOne{t: t}
}

func (r *result) setResAt(index int) *Test {
	r.t.res[index] = r.res
	return r.t
}
func (r *resultOne) setResAt(index int) *Test {
	r.t.res[index] = 1
	return r.t
}

func (r *result) containers() *Test {
	return r.setResAt(0)
}
func (ro resultOne) container() *Test {
	return ro.setResAt(0)
}

func (r *result) volumes() *Test {
	return r.setResAt(2)
}
func (r *result) orphanedVolumes() *Test {
	return r.setResAt(3)
}

func (r *result) markers() *Test {
	return r.setResAt(4)
}

func (t *Test) mustProduce(strs []string) *Test {
	t.strs = strs
	return t
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		switch {
		case strings.HasSuffix(spec, "/"):
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		case strings.HasSuffix(spec, "@"):
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"

		default:
			res = res + spec + "\n"
		}
	}
	return res
}

func (t *Test) inspectVolumes() string {
	if len(t.cs) == 0 {
		return ""
	}
	res := t.cs[t.ci]
	t.ci = t.ci + 1
	return res
}

var deletions = []string{}
var tests = []*Test{
	newTest("empty vfs"),
	newTest("2 valid containers without any volume").
		setContainersPs([]string{"/contA,", "/contB,"}).
		expects(2).containers().
		mustProduce([]string{"cnt 'contA' (x)[false] - 0 vol", "cnt 'contB' (x)[false] - 0 vol"}),
	newTest("2 valid volumes").
		setVolumesLs([]string{"fa/", "fb/"}).
		expects(2).volumes().
		expects(2).orphanedVolumes().
		mustProduce([]string{"vol 'fa00000'<<nil>>", "vol 'fb11111'<<nil>>"}),
	newTest("Invalid (ill-formed) markers must be deleted").
		setVolumesLs([]string{"cainv/path/a@"}).
		expects(-1).markers(),
	newTest("Invalid (no readlink) markers must be deleted").
		setVolumesLs([]string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}).
		expects(-2).markers(),
	newTest("Invalid (no ls) markers must be deleted").
		setVolumesLs([]string{"ca;/path/nolsa@", "cb;/path/nolsb@"}).
		expects(-2).markers(),
	newTest("Invalid (no vdir) markers must be deleted").
		setVolumesLs([]string{"ca$novdira;/path/nolsa@", "cb$novdirb;/path/nolsb@"}).
		expects(-2).markers(),
	/*
		Test{"two valid markers", []string{"ca$fa;/path/vola@", "cb$fb;/path/volb@"}, []int{0, 0, 0, 0, 2}, []string{"marker 'fa11111'<ca$fa->/path/vola>", "marker 'fb11111'<cb$fb->/path/volb>"}},
		Test{"Invalid (bad name) volume", []string{"inva/"}, []int{0, 0, -1, 0, 0}, []string{}},
		Test{"Invalid file in volume vfs dir", []string{"invf"}, []int{0, 0, -1, 0, 0}, []string{}},
	*/
}
var currenttest *Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		fmt.Println("------ vvv " + test.title + " vvv ------")
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if nbvolumes(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], nbvolumes(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}

		for _, c := range tc {
			cs := c.String()
			check(cs, "container", test, t, i)
		}
		for _, v := range tv {
			vs := v.String()
			check(vs, "volume", test, t, i)
		}
		for _, m := range tm {
			ms := m.String()
			check(ms, "marker", test, t, i)
		}
		fmt.Println("------ ^^^ " + test.title + " ^^^ ------")
		fmt.Println("----------")
	}
}

func check(s string, tmsg string, test *Test, t *testing.T, i int) {
	found := false
	for _, tms := range test.strs {
		if s == tms {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("Test %d: '%s' expected %s >%s<, not found", i+1, test.title, tmsg, s)
	}

}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}

func nbvolumes(vm volumes) int {
	res := len(vm)
	for _, d := range deletions {
		if !strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<|endoftext|>


<commit_before>package fakes

import "github.com/cloudfoundry/bosh-bootloader/storage"

type TemplateGenerator struct {
	GenerateCall struct {
		CallCount int
		Receives  struct {
			State storage.State
		}
		Returns struct {
			Template string
		}
	}
}

func (g *TemplateGenerator) Generate(state storage.State) string {
	g.GenerateCall.CallCount++
	g.GenerateCall.Receives.State = state
	return g.GenerateCall.Returns.Template
}
<commit_msg>Change function receiver variable in fake template generator<commit_after>package fakes

import "github.com/cloudfoundry/bosh-bootloader/storage"

type TemplateGenerator struct {
	GenerateCall struct {
		CallCount int
		Receives  struct {
			State storage.State
		}
		Returns struct {
			Template string
		}
	}
}

func (t *TemplateGenerator) Generate(state storage.State) string {
	t.GenerateCall.CallCount++
	t.GenerateCall.Receives.State = state
	return t.GenerateCall.Returns.Template
}
<|endoftext|>