<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/jrperritt/rack/util"
	"gopkg.in/ini.v1"
)

func configure(c *cli.Context) {
	intro := []string{"\nThis interactive session will walk you through creating",
		"a profile in your configuration file. You may fill in all or none of the",
		"values.\n"}
	fmt.Println(strings.Join(intro, "\n"))
	reader := bufio.NewReader(os.Stdin)
	m := map[string]string{
		"username": "",
		"apikey":   "",
		"region":   "",
	}
	fmt.Print("Rackspace Username: ")
	username, _ := reader.ReadString('\n')
	m["username"] = strings.TrimSuffix(username, string('\n'))

	fmt.Print("Rackspace API key: ")
	apiKey, _ := reader.ReadString('\n')
	m["apikey"] = strings.TrimSuffix(apiKey, string('\n'))

	fmt.Print("Rackspace Region : ")
	region, _ := reader.ReadString('\n')
	m["region"] = strings.ToUpper(strings.TrimSuffix(region, string('\n')))

	fmt.Print("Profile Name (leave blank to create a default profile): ")
	profile, _ := reader.ReadString('\n')
	profile = strings.TrimSuffix(profile, string('\n'))

	configFile, err := configFile()
	var cfg *ini.File
	cfg, err = ini.Load(configFile)
	if err != nil {
		// fmt.Printf("Error loading config file: %s\n", err)
		cfg = ini.Empty()
	}

	if profile == "" || strings.ToLower(profile) == "default" {
		profile = "DEFAULT"
	}

	for {
		if _, err := cfg.GetSection(profile); err == nil {
			fmt.Printf("\nA profile named %s already exists. Overwrite? (y/n): ", profile)
			choice, _ := reader.ReadString('\n')
			choice = strings.TrimSuffix(choice, string('\n'))
			switch strings.ToLower(choice) {
			case "y", "yes":
				break
			case "n", "no":
				fmt.Print("Profile Name: ")
				profile, _ := reader.ReadString('\n')
				profile = strings.TrimSuffix(profile, string('\n'))
				continue
			default:
				continue
			}
			break
		}
		break
	}

	section, err := cfg.NewSection(profile)
	if err != nil {
		//fmt.Printf("Error creating new section [%s] in config file: %s\n", profile, err)
		return
	}

	for key, val := range m {
		section.NewKey(key, val)
	}

	err = cfg.SaveTo(configFile)
	if err != nil {
		//fmt.Printf("Error saving config file: %s\n", err)
		return
	}
}

func configFile() (string, error) {
	dir, err := util.RackDir()
	if err != nil {
		return "", fmt.Errorf("Error reading from cache: %s", err)
	}
	filepath := path.Join(dir, "config")
	// check if the cache file exists
	if _, err := os.Stat(filepath); err == nil {
		return filepath, nil
	}
	// create the cache file if it doesn't already exist
	f, err := os.Create(filepath)
	defer f.Close()
	return filepath, err
}
<commit_msg>trim space around profile name<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/jrperritt/rack/util"
	"gopkg.in/ini.v1"
)

func configure(c *cli.Context) {
	intro := []string{"\nThis interactive session will walk you through creating",
		"a profile in your configuration file. You may fill in all or none of the",
		"values.\n"}
	fmt.Println(strings.Join(intro, "\n"))
	reader := bufio.NewReader(os.Stdin)
	m := map[string]string{
		"username": "",
		"apikey":   "",
		"region":   "",
	}
	fmt.Print("Rackspace Username: ")
	username, _ := reader.ReadString('\n')
	m["username"] = strings.TrimSuffix(username, string('\n'))

	fmt.Print("Rackspace API key: ")
	apiKey, _ := reader.ReadString('\n')
	m["apikey"] = strings.TrimSuffix(apiKey, string('\n'))

	fmt.Print("Rackspace Region : ")
	region, _ := reader.ReadString('\n')
	m["region"] = strings.ToUpper(strings.TrimSuffix(region, string('\n')))

	fmt.Print("Profile Name (leave blank to create a default profile): ")
	profile, _ := reader.ReadString('\n')
	profile = strings.TrimSuffix(profile, string('\n'))

	configFile, err := configFile()
	var cfg *ini.File
	cfg, err = ini.Load(configFile)
	if err != nil {
		// fmt.Printf("Error loading config file: %s\n", err)
		cfg = ini.Empty()
	}

	if strings.TrimSpace(profile) == "" || strings.ToLower(profile) == "default" {
		profile = "DEFAULT"
	}

	for {
		if _, err := cfg.GetSection(profile); err == nil {
			fmt.Printf("\nA profile named %s already exists. Overwrite? (y/n): ", profile)
			choice, _ := reader.ReadString('\n')
			choice = strings.TrimSuffix(choice, string('\n'))
			switch strings.ToLower(choice) {
			case "y", "yes":
				break
			case "n", "no":
				fmt.Print("Profile Name: ")
				profile, _ := reader.ReadString('\n')
				profile = strings.TrimSuffix(profile, string('\n'))
				continue
			default:
				continue
			}
			break
		}
		break
	}

	section, err := cfg.NewSection(profile)
	if err != nil {
		//fmt.Printf("Error creating new section [%s] in config file: %s\n", profile, err)
		return
	}

	for key, val := range m {
		section.NewKey(key, val)
	}

	err = cfg.SaveTo(configFile)
	if err != nil {
		//fmt.Printf("Error saving config file: %s\n", err)
		return
	}
}

func configFile() (string, error) {
	dir, err := util.RackDir()
	if err != nil {
		return "", fmt.Errorf("Error reading from cache: %s", err)
	}
	filepath := path.Join(dir, "config")
	// check if the cache file exists
	if _, err := os.Stat(filepath); err == nil {
		return filepath, nil
	}
	// create the cache file if it doesn't already exist
	f, err := os.Create(filepath)
	defer f.Close()
	return filepath, err
}
<|endoftext|>


<commit_before>// Shared server connections between different clients.

package main

import (
	"sync"
	"time"
)

// Maximum number of connections to a server.
const maxServerConnCnt = 20

// Store each server's connections in separate channels, getting
// connections for different servers can be done in parallel.
type ConnPool struct {
	idleConn map[string]chan *serverConn
	muxConn  chan *serverConn // connections support multiplexing
	sync.RWMutex
}

var connPool = &ConnPool{
	idleConn: map[string]chan *serverConn{},
	muxConn:  make(chan *serverConn, maxServerConnCnt),
}

func getConnFromChan(ch chan *serverConn) (sv *serverConn) {
	for {
		select {
		case sv = <-ch:
			if sv.mayBeClosed() {
				sv.Close()
				continue
			}
			return sv
		default:
			return nil
		}
	}
}

func putConnToChan(sv *serverConn, ch chan *serverConn) {
	select {
	case ch <- sv:
		debug.Printf("connPool channel %s: put back conn\n", sv.hostPort)
		return
	default:
		// Simply close the connection if can't put into channel immediately.
		// A better solution would remove old connections from the channel and
		// add the new one. But's it's more complicated and this should happen
		// rarely.
		debug.Printf("connPool %s: channel full", sv.hostPort)
		sv.Close()
	}
}

func (cp *ConnPool) Get(hostPort string) *serverConn {
	cp.RLock()
	ch, ok := cp.idleConn[hostPort]
	cp.RUnlock()

	if !ok {
		return nil
	}

	// get connection from connections of existing host
	if sv := getConnFromChan(ch); sv != nil {
		debug.Printf("connPool get site-specific conn")
		return sv
	}
	// get connection from multiplexing connection pool
	sv := getConnFromChan(cp.muxConn)
	if sv != nil {
		debug.Printf("connPool get mux conn")
	}
	return sv
}

func (cp *ConnPool) Put(sv *serverConn) {
	var ch chan *serverConn

	cp.RLock()
	ch, ok := cp.idleConn[sv.hostPort]
	cp.RUnlock()

	if !ok {
		debug.Printf("connPool %s: new channel", sv.hostPort)
		ch = make(chan *serverConn, maxServerConnCnt)
		ch <- sv
		cp.Lock()
		cp.idleConn[sv.hostPort] = ch
		cp.Unlock()
		// start a new goroutine to close stale server connections
		go closeStaleServerConn(ch, sv.hostPort)
		return
	}

	switch sv.Conn.(type) {
	case httpConn:
		// multiplexing connections
		debug.Printf("connPool put back mux conn")
		putConnToChan(sv, cp.muxConn)
	default:
		// site-specific connections
		debug.Printf("connPool put back site-specific conn")
		putConnToChan(sv, ch)
	}
}

func closeStaleServerConn(ch chan *serverConn, hostPort string) {
	// Tricky here. When removing a channel from the map, there maybe
	// goroutines doing Put and Get using that channel.

	// For Get, there's no problem because it will return immediately.
	// For Put, it's possible that a new connection is added to the
	// channel, but the channel is no longer in the map.
	// So after removed the channel from the map, we wait for several seconds
	// and then close all connections left in it.

	// It's possible that Put add the connection after the final wait, but
	// that should not happen in practice, and the worst result is just lost
	// some memory and open fd.
done:
	for {
		time.Sleep(defaultServerConnTimeout)
	cleanup:
		for {
			select {
			case sv := <-ch:
				if sv.mayBeClosed() {
					debug.Printf("connPool channel %s: close one conn\n", hostPort)
					sv.Close()
				} else {
					// Put it back and wait.
					debug.Printf("connPool channel %s: put back conn\n", hostPort)
					ch <- sv
					break cleanup
				}
			default:
				// no more connection in this channel
				// remove the channel from the map
				connPool.Lock()
				delete(connPool.idleConn, hostPort)
				connPool.Unlock()
				debug.Printf("connPool channel %s: removed\n", hostPort)
				break done
			}
		}
	}
	// Final wait and then close all left connections. In practice, there
	// should be no other goroutines holding reference to the channel.
	time.Sleep(2 * time.Second)
	for {
		select {
		case sv := <-ch:
			debug.Printf("connPool channel %s: close conn after removed\n", hostPort)
			sv.Close()
		default:
			debug.Printf("connPool channel %s: cleanup done\n", hostPort)
			return
		}
	}
}
<commit_msg>Fix for multiplexing http parent connection.<commit_after>// Shared server connections between different clients.

package main

import (
	"sync"
	"time"
)

// Maximum number of connections to a server.
const maxServerConnCnt = 20

// Store each server's connections in separate channels, getting
// connections for different servers can be done in parallel.
type ConnPool struct {
	idleConn map[string]chan *serverConn
	muxConn  chan *serverConn // connections support multiplexing
	sync.RWMutex
}

var connPool = &ConnPool{
	idleConn: map[string]chan *serverConn{},
	muxConn:  make(chan *serverConn, 35),
}

func getConnFromChan(ch chan *serverConn) (sv *serverConn) {
	for {
		select {
		case sv = <-ch:
			if sv.mayBeClosed() {
				sv.Close()
				continue
			}
			return sv
		default:
			return nil
		}
	}
}

func putConnToChan(sv *serverConn, ch chan *serverConn, chname string) {
	select {
	case ch <- sv:
		debug.Printf("connPool channel %s: put conn\n", chname)
		return
	default:
		// Simply close the connection if can't put into channel immediately.
		// A better solution would remove old connections from the channel and
		// add the new one. But's it's more complicated and this should happen
		// rarely.
		debug.Printf("connPool channel %s: full", chname)
		sv.Close()
	}
}

func (cp *ConnPool) Get(hostPort string) (sv *serverConn) {
	// Get from site specific connection first.
	// Direct connection are all site specific, so must use site specific
	// first to avoid using parent proxy for direct sites.
	cp.RLock()
	ch := cp.idleConn[hostPort]
	cp.RUnlock()

	if ch != nil {
		sv = getConnFromChan(ch)
	}
	if sv != nil {
		debug.Printf("connPool %s: get conn\n", hostPort)
		return sv
	}

	// Get connection from multiplexing connection pool.
	// All mulplexing connections are for blocked sites.
	sv = getConnFromChan(cp.muxConn)
	if bool(debug) && sv != nil {
		debug.Println("connPool mux: get conn", hostPort)
	}
	return sv
}

func (cp *ConnPool) Put(sv *serverConn) {
	// Multiplexing connections.
	switch sv.Conn.(type) {
	case httpConn:
		putConnToChan(sv, cp.muxConn, "mux")
		return
	}

	// Site specific connections.
	cp.RLock()
	ch := cp.idleConn[sv.hostPort]
	cp.RUnlock()

	if ch == nil {
		debug.Printf("connPool %s: new channel\n", sv.hostPort)
		ch = make(chan *serverConn, maxServerConnCnt)
		ch <- sv
		cp.Lock()
		cp.idleConn[sv.hostPort] = ch
		cp.Unlock()
		// start a new goroutine to close stale server connections
		go closeStaleServerConn(ch, sv.hostPort)
	} else {
		putConnToChan(sv, ch, sv.hostPort)
	}
}

func closeStaleServerConn(ch chan *serverConn, hostPort string) {
	// Tricky here. When removing a channel from the map, there maybe
	// goroutines doing Put and Get using that channel.

	// For Get, there's no problem because it will return immediately.
	// For Put, it's possible that a new connection is added to the
	// channel, but the channel is no longer in the map.
	// So after removed the channel from the map, we wait for several seconds
	// and then close all connections left in it.

	// It's possible that Put add the connection after the final wait, but
	// that should not happen in practice, and the worst result is just lost
	// some memory and open fd.
done:
	for {
		time.Sleep(defaultServerConnTimeout)
	cleanup:
		for {
			select {
			case sv := <-ch:
				if sv.mayBeClosed() {
					debug.Printf("connPool channel %s: close one conn\n", hostPort)
					sv.Close()
				} else {
					// Put it back and wait.
					debug.Printf("connPool channel %s: put back conn\n", hostPort)
					ch <- sv
					break cleanup
				}
			default:
				// no more connection in this channel
				// remove the channel from the map
				connPool.Lock()
				delete(connPool.idleConn, hostPort)
				connPool.Unlock()
				debug.Printf("connPool channel %s: removed\n", hostPort)
				break done
			}
		}
	}
	// Final wait and then close all left connections. In practice, there
	// should be no other goroutines holding reference to the channel.
	time.Sleep(2 * time.Second)
	for {
		select {
		case sv := <-ch:
			debug.Printf("connPool channel %s: close conn after removed\n", hostPort)
			sv.Close()
		default:
			debug.Printf("connPool channel %s: cleanup done\n", hostPort)
			return
		}
	}
}
<|endoftext|>


<commit_before>package raft

import (
	"fmt"
	"math/rand"
	"sync/atomic"
	"time"
)

type TimeSettings struct {
	TickerDuration time.Duration

	// Election timeout low value - 2x this value is used as high value.
	ElectionTimeoutLow time.Duration
}

const (
	RPC_CHANNEL_BUFFER_SIZE = 100
)

type ConsensusModule struct {
	// -- External components - these fields meant to be immutable
	persistentState PersistentState
	log             Log
	rpcSender       RpcSender

	// -- Config - these fields meant to be immutable
	thisServerId           ServerId
	peerServerIds          []ServerId
	electionTimeoutLow     time.Duration
	currentElectionTimeout time.Duration

	// -- State - these fields may be accessed concurrently
	stopped     int32
	serverState ServerState

	// -- State - these fields meant for use within the goroutine
	volatileState          VolatileState
	electionTimeoutTime    time.Time
	candidateVolatileState *CandidateVolatileState

	// -- Channels
	rpcChannel chan rpcTuple
	ticker     *time.Ticker

	// -- Control
	stopSignal chan struct{}
	stopError  *atomic.Value
}

// Initialize a consensus module with the given components and settings.
// A goroutine that handles consensus processing is created.
// All parameters are required and cannot be nil.
// Server ids are check using ValidateServerIds().
// Time settings is checked using ValidateTimeSettings().
func NewConsensusModule(
	persistentState PersistentState,
	log Log,
	rpcSender RpcSender,
	thisServerId ServerId,
	peerServerIds []ServerId,
	timeSettings TimeSettings,
) *ConsensusModule {
	// Param checks
	if persistentState == nil {
		panic("'persistentState' cannot be nil")
	}
	if log == nil {
		panic("'log' cannot be nil")
	}
	if rpcSender == nil {
		panic("'rpcSender' cannot be nil")
	}
	if emsg := ValidateServerIds(thisServerId, peerServerIds); emsg != "" {
		panic(emsg)
	}
	if emsg := ValidateTimeSettings(timeSettings); emsg != "" {
		panic(emsg)
	}

	now := time.Now()
	rpcChannel := make(chan rpcTuple, RPC_CHANNEL_BUFFER_SIZE)
	ticker := time.NewTicker(timeSettings.TickerDuration)

	cm := &ConsensusModule{
		// -- External components
		persistentState,
		log,
		rpcSender,

		// -- Config
		thisServerId,
		peerServerIds,
		timeSettings.ElectionTimeoutLow,
		0, // temp value, to be replaced before goroutine start

		// -- State
		0,
		// #5.2-p1s2: When servers start up, they begin as followers
		FOLLOWER,

		// -- State
		VolatileState{},
		now, // temp value, to be replaced before goroutine start
		nil,

		// -- Channels
		rpcChannel,
		ticker,

		// -- Control
		make(chan struct{}),
		&atomic.Value{},
	}

	cm.chooseNewRandomElectionTimeout()
	cm.resetElectionTimeoutTime(now)

	// Start the go routine
	go cm.processor()

	return cm
}

// Check if the goroutine is stopped.
func (cm *ConsensusModule) IsStopped() bool {
	return atomic.LoadInt32(&cm.stopped) != 0
}

// Stop the consensus module asynchronously.
// This will stop the goroutine that does the processing.
// Safe to call even if the goroutine has stopped.
// Will panic if called more than once.
func (cm *ConsensusModule) StopAsync() {
	close(cm.stopSignal)
}

// Get the panic error value that stopped the goroutine.
// The value will be nil if the goroutine is not stopped, or stopped
// without an error, or  panicked with a nil value.
func (cm *ConsensusModule) GetStopError() interface{} {
	return cm.stopError.Load()
}

// Get the current server state
func (cm *ConsensusModule) GetServerState() ServerState {
	return ServerState(atomic.LoadUint32((*uint32)(&cm.serverState)))
}

// Process the given RPC message from the given peer asynchronously.
// See comments in rpctypes.go for an explanation of the messaging model.
// This method sends the rpc to the ConsensusModule's goroutine.
// Sending an unknown or unexpected rpc message will cause the
// ConsensusModule goroutine to panic and stop.
func (cm *ConsensusModule) ProcessRpcAsync(from ServerId, rpc interface{}) {
	select {
	case cm.rpcChannel <- rpcTuple{from, rpc}:
	default:
		// FIXME
		panic("oops! rpcChannel is full!")
	}
}

type rpcTuple struct {
	from ServerId
	rpc  interface{}
}

// -- protected methods

// Set the current server state
func (cm *ConsensusModule) setServerState(serverState ServerState) {
	if serverState != FOLLOWER && serverState != CANDIDATE && serverState != LEADER {
		panic(fmt.Sprintf("FATAL: unknown ServerState: %v", serverState))
	}
	atomic.StoreUint32((*uint32)(&cm.serverState), (uint32)(serverState))
}

func (cm *ConsensusModule) chooseNewRandomElectionTimeout() {
	// #5.2-p6s2: ..., election timeouts are chosen randomly from a fixed
	// interval (e.g., 150-300ms)
	cm.currentElectionTimeout = cm.electionTimeoutLow + time.Duration(rand.Int63n(int64(cm.electionTimeoutLow)+1))
}

func (cm *ConsensusModule) resetElectionTimeoutTime(now time.Time) {
	cm.electionTimeoutTime = now.Add(cm.currentElectionTimeout)
}

func (cm *ConsensusModule) processor() {
	defer func() {
		// Recover & save the panic reason
		if r := recover(); r != nil {
			cm.stopError.Store(r)
		}
		// Mark the server as stopped
		atomic.StoreInt32(&cm.stopped, 1)
		// Clean up things
		close(cm.rpcChannel)
		cm.ticker.Stop()
		// TODO: call stop event listener(s)
	}()

loop:
	for {
		select {
		case rpc, ok := <-cm.rpcChannel:
			if !ok {
				panic("FATAL: rpcChannel closed")
			}
			cm.rpc(rpc.from, rpc.rpc)
		case now, ok := <-cm.ticker.C:
			if !ok {
				panic("FATAL: ticker channel closed")
			}
			cm.tick(now)
		case <-cm.stopSignal:
			break loop
		}
	}
}

func (cm *ConsensusModule) rpc(from ServerId, rpc interface{}) {
	switch rpc := rpc.(type) {

	case *AppendEntries:
		success := cm._processRpc_AppendEntries(rpc)
		reply := &AppendEntriesReply{
			cm.persistentState.GetCurrentTerm(),
			success,
		}
		cm.rpcSender.SendAsync(from, reply)
	case *RpcRequestVoteReply:
		cm._processRpc_RequestVoteReply(from, rpc)
	default:
		panic(fmt.Sprintf("FATAL: unknown rpc type: %T", rpc))
	}
}

func (cm *ConsensusModule) tick(now time.Time) {
	serverState := cm.GetServerState()
	switch serverState {
	case FOLLOWER:
		// #5.2-p1s5: If a follower receives no communication over a period
		// of time called the election timeout, then it assumes there is no
		// viable leader and begins an election to choose a new leader.
		if now.After(cm.electionTimeoutTime) {
			cm.beginElection(now)
		}
	case CANDIDATE:
		// #5.2-p5s1: The third possible outcome is that a candidate neither
		// wins nor loses the election; ... votes could be split so that no
		// candidate obtains a majority.
		// #5.2-p5s2: When this happens, each candidate will time out and
		// start a new election by incrementing its term and initiating
		// another round of RequestVote RPCs.
		if now.After(cm.electionTimeoutTime) {
			cm.beginElection(now)
		}
		// TODO: else/and anything else?
	case LEADER:
		// FIXME
		panic("todo: LEADER tick()")
	default:
		panic(fmt.Sprintf("FATAL: unknown ServerState: %v", serverState))
	}
}

func (cm *ConsensusModule) beginElection(now time.Time) {
	// #5.2-p2s1: To begin an election, a follower increments its
	// current term and transitions to candidate state.
	newTerm := cm.persistentState.GetCurrentTerm() + 1
	cm.candidateVolatileState = newCandidateVolatileState(cm.peerServerIds)
	cm.setServerState(CANDIDATE)
	// #5.2-p2s2: It then votes for itself and issues RequestVote RPCs
	// in parallel to each of the other servers in the cluster.
	cm.persistentState.SetCurrentTermAndVotedFor(newTerm, cm.thisServerId)
	lastLogIndex := cm.log.getIndexOfLastEntry()
	var lastLogTerm TermNo
	if lastLogIndex > 0 {
		lastLogTerm = cm.log.getTermAtIndex(lastLogIndex)
	} else {
		lastLogTerm = 0
	}
	for _, serverId := range cm.peerServerIds {
		rpcRequestVote := &RpcRequestVote{newTerm, lastLogIndex, lastLogTerm}
		cm.rpcSender.SendAsync(serverId, rpcRequestVote)
	}
	// Reset election timeout!
	cm.chooseNewRandomElectionTimeout()
	cm.resetElectionTimeoutTime(now)
}
<commit_msg>Split passive constructor<commit_after>package raft

import (
	"fmt"
	"math/rand"
	"sync/atomic"
	"time"
)

type TimeSettings struct {
	TickerDuration time.Duration

	// Election timeout low value - 2x this value is used as high value.
	ElectionTimeoutLow time.Duration
}

const (
	RPC_CHANNEL_BUFFER_SIZE = 100
)

type ConsensusModule struct {
	// -- External components - these fields meant to be immutable
	persistentState PersistentState
	log             Log
	rpcSender       RpcSender

	// -- Config - these fields meant to be immutable
	thisServerId           ServerId
	peerServerIds          []ServerId
	electionTimeoutLow     time.Duration
	currentElectionTimeout time.Duration

	// -- State - these fields may be accessed concurrently
	stopped     int32
	serverState ServerState

	// -- State - these fields meant for use within the goroutine
	volatileState          VolatileState
	electionTimeoutTime    time.Time
	candidateVolatileState *CandidateVolatileState

	// -- Channels
	rpcChannel chan rpcTuple
	ticker     *time.Ticker

	// -- Control
	stopSignal chan struct{}
	stopError  *atomic.Value
}

// Initialize a consensus module with the given components and settings.
// A goroutine that handles consensus processing is created.
// All parameters are required and cannot be nil.
// Server ids are check using ValidateServerIds().
// Time settings is checked using ValidateTimeSettings().
func NewConsensusModule(
	persistentState PersistentState,
	log Log,
	rpcSender RpcSender,
	thisServerId ServerId,
	peerServerIds []ServerId,
	timeSettings TimeSettings,
) *ConsensusModule {
	cm, _ := newPassiveConsensusModule(
		persistentState,
		log,
		rpcSender,
		thisServerId,
		peerServerIds,
		timeSettings,
	)

	// Start the go routine
	go cm.processor()

	return cm
}

func newPassiveConsensusModule(
	persistentState PersistentState,
	log Log,
	rpcSender RpcSender,
	thisServerId ServerId,
	peerServerIds []ServerId,
	timeSettings TimeSettings,
) (*ConsensusModule, time.Time) {
	// Param checks
	if persistentState == nil {
		panic("'persistentState' cannot be nil")
	}
	if log == nil {
		panic("'log' cannot be nil")
	}
	if rpcSender == nil {
		panic("'rpcSender' cannot be nil")
	}
	if emsg := ValidateServerIds(thisServerId, peerServerIds); emsg != "" {
		panic(emsg)
	}
	if emsg := ValidateTimeSettings(timeSettings); emsg != "" {
		panic(emsg)
	}

	now := time.Now()
	rpcChannel := make(chan rpcTuple, RPC_CHANNEL_BUFFER_SIZE)
	ticker := time.NewTicker(timeSettings.TickerDuration)

	cm := &ConsensusModule{
		// -- External components
		persistentState,
		log,
		rpcSender,

		// -- Config
		thisServerId,
		peerServerIds,
		timeSettings.ElectionTimeoutLow,
		0, // temp value, to be replaced before goroutine start

		// -- State
		0,
		// #5.2-p1s2: When servers start up, they begin as followers
		FOLLOWER,

		// -- State
		VolatileState{},
		now, // temp value, to be replaced before goroutine start
		nil,

		// -- Channels
		rpcChannel,
		ticker,

		// -- Control
		make(chan struct{}),
		&atomic.Value{},
	}

	cm.chooseNewRandomElectionTimeout()
	cm.resetElectionTimeoutTime(now)

	return cm, now
}

// Check if the goroutine is stopped.
func (cm *ConsensusModule) IsStopped() bool {
	return atomic.LoadInt32(&cm.stopped) != 0
}

// Stop the consensus module asynchronously.
// This will stop the goroutine that does the processing.
// Safe to call even if the goroutine has stopped.
// Will panic if called more than once.
func (cm *ConsensusModule) StopAsync() {
	close(cm.stopSignal)
}

// Get the panic error value that stopped the goroutine.
// The value will be nil if the goroutine is not stopped, or stopped
// without an error, or  panicked with a nil value.
func (cm *ConsensusModule) GetStopError() interface{} {
	return cm.stopError.Load()
}

// Get the current server state
func (cm *ConsensusModule) GetServerState() ServerState {
	return ServerState(atomic.LoadUint32((*uint32)(&cm.serverState)))
}

// Process the given RPC message from the given peer asynchronously.
// See comments in rpctypes.go for an explanation of the messaging model.
// This method sends the rpc to the ConsensusModule's goroutine.
// Sending an unknown or unexpected rpc message will cause the
// ConsensusModule goroutine to panic and stop.
func (cm *ConsensusModule) ProcessRpcAsync(from ServerId, rpc interface{}) {
	select {
	case cm.rpcChannel <- rpcTuple{from, rpc}:
	default:
		// FIXME
		panic("oops! rpcChannel is full!")
	}
}

type rpcTuple struct {
	from ServerId
	rpc  interface{}
}

// -- protected methods

// Set the current server state
func (cm *ConsensusModule) setServerState(serverState ServerState) {
	if serverState != FOLLOWER && serverState != CANDIDATE && serverState != LEADER {
		panic(fmt.Sprintf("FATAL: unknown ServerState: %v", serverState))
	}
	atomic.StoreUint32((*uint32)(&cm.serverState), (uint32)(serverState))
}

func (cm *ConsensusModule) chooseNewRandomElectionTimeout() {
	// #5.2-p6s2: ..., election timeouts are chosen randomly from a fixed
	// interval (e.g., 150-300ms)
	cm.currentElectionTimeout = cm.electionTimeoutLow + time.Duration(rand.Int63n(int64(cm.electionTimeoutLow)+1))
}

func (cm *ConsensusModule) resetElectionTimeoutTime(now time.Time) {
	cm.electionTimeoutTime = now.Add(cm.currentElectionTimeout)
}

func (cm *ConsensusModule) processor() {
	defer func() {
		// Recover & save the panic reason
		if r := recover(); r != nil {
			cm.stopError.Store(r)
		}
		// Mark the server as stopped
		atomic.StoreInt32(&cm.stopped, 1)
		// Clean up things
		close(cm.rpcChannel)
		cm.ticker.Stop()
		// TODO: call stop event listener(s)
	}()

loop:
	for {
		select {
		case rpc, ok := <-cm.rpcChannel:
			if !ok {
				panic("FATAL: rpcChannel closed")
			}
			cm.rpc(rpc.from, rpc.rpc)
		case now, ok := <-cm.ticker.C:
			if !ok {
				panic("FATAL: ticker channel closed")
			}
			cm.tick(now)
		case <-cm.stopSignal:
			break loop
		}
	}
}

func (cm *ConsensusModule) rpc(from ServerId, rpc interface{}) {
	switch rpc := rpc.(type) {

	case *AppendEntries:
		success := cm._processRpc_AppendEntries(rpc)
		reply := &AppendEntriesReply{
			cm.persistentState.GetCurrentTerm(),
			success,
		}
		cm.rpcSender.SendAsync(from, reply)
	case *RpcRequestVoteReply:
		cm._processRpc_RequestVoteReply(from, rpc)
	default:
		panic(fmt.Sprintf("FATAL: unknown rpc type: %T", rpc))
	}
}

func (cm *ConsensusModule) tick(now time.Time) {
	serverState := cm.GetServerState()
	switch serverState {
	case FOLLOWER:
		// #5.2-p1s5: If a follower receives no communication over a period
		// of time called the election timeout, then it assumes there is no
		// viable leader and begins an election to choose a new leader.
		if now.After(cm.electionTimeoutTime) {
			cm.beginElection(now)
		}
	case CANDIDATE:
		// #5.2-p5s1: The third possible outcome is that a candidate neither
		// wins nor loses the election; ... votes could be split so that no
		// candidate obtains a majority.
		// #5.2-p5s2: When this happens, each candidate will time out and
		// start a new election by incrementing its term and initiating
		// another round of RequestVote RPCs.
		if now.After(cm.electionTimeoutTime) {
			cm.beginElection(now)
		}
		// TODO: else/and anything else?
	case LEADER:
		// FIXME
		panic("todo: LEADER tick()")
	default:
		panic(fmt.Sprintf("FATAL: unknown ServerState: %v", serverState))
	}
}

func (cm *ConsensusModule) beginElection(now time.Time) {
	// #5.2-p2s1: To begin an election, a follower increments its
	// current term and transitions to candidate state.
	newTerm := cm.persistentState.GetCurrentTerm() + 1
	cm.candidateVolatileState = newCandidateVolatileState(cm.peerServerIds)
	cm.setServerState(CANDIDATE)
	// #5.2-p2s2: It then votes for itself and issues RequestVote RPCs
	// in parallel to each of the other servers in the cluster.
	cm.persistentState.SetCurrentTermAndVotedFor(newTerm, cm.thisServerId)
	lastLogIndex := cm.log.getIndexOfLastEntry()
	var lastLogTerm TermNo
	if lastLogIndex > 0 {
		lastLogTerm = cm.log.getTermAtIndex(lastLogIndex)
	} else {
		lastLogTerm = 0
	}
	for _, serverId := range cm.peerServerIds {
		rpcRequestVote := &RpcRequestVote{newTerm, lastLogIndex, lastLogTerm}
		cm.rpcSender.SendAsync(serverId, rpcRequestVote)
	}
	// Reset election timeout!
	cm.chooseNewRandomElectionTimeout()
	cm.resetElectionTimeoutTime(now)
}
<|endoftext|>


<commit_before>// +build armbe mips mips64p32

package ebpf

import (
	"unsafe"
)

// ptr wraps an unsafe.Pointer to be 64bit to
// conform to the syscall specification.
type syscallPtr struct {
	ptr unsafe.Pointer
	pad uint32
}
<commit_msg>syscalls: fix pointer padding on 32bit big endian arches<commit_after>// +build armbe mips mips64p32

package ebpf

import (
	"unsafe"
)

// ptr wraps an unsafe.Pointer to be 64bit to
// conform to the syscall specification.
type syscallPtr struct {
	pad uint32
	ptr unsafe.Pointer
}
<|endoftext|>


<commit_before>package parse

import "strings"

//const noID = -42

var (
	EOF  = Term("EOF")
	Null = Term("Null")
	Self = Term("Self")
)

func newR() *R {
	return &R{}
}

func Term(name string) *R {
	return newR().As(name)
}

func Rule(name string, rules ...*R) *R {
	r := Con(rules...)
	r.initRecursiveRule(make(map[*R]bool), r)
	return r.As(name)
}
func (r *R) initRecursiveRule(m map[*R]bool, selfValue *R) {
	if m[r] {
		return
	}
	m[r] = true
	for _, alt := range r.Alts {
		if alt.Parent == Self {
			alt.Parent = selfValue
		}
		for i := range alt.Rules {
			if alt.Rules[i] == Self {
				alt.Rules[i] = selfValue
			} else {
				alt.Rules[i].initRecursiveRule(m, selfValue)
			}
		}
	}
}

func Con(rules ...*R) *R {
	if len(rules) == 1 {
		return rules[0]
	}
	r := newR().As(Rules(rules).expr(" "))
	r.Alts = Alts{{r, rules}}
	return r
}

func Or(rules ...*R) *R {
	if len(rules) == 1 {
		return rules[0]
	}
	r := newR().As("(" + Rules(rules).expr("|") + ")")
	r.Alts = make(Alts, len(rules))
	for i := range rules {
		r.Alts[i] = rules[i].toAlt(r)
	}
	return r
}
func (r *R) toAlt(parent *R) *Alt {
	if len(r.Alts) == 1 {
		return &Alt{parent, r.Alts[0].Rules}
	}
	return &Alt{parent, Rules{r}}
}

func (r *R) As(name string) *R {
	r.Name = name
	return r
}

func (r *R) ZeroOrOne() *R {
	return Or(r, Null).As(parens(r.Name) + "?")
}

func (r *R) OneOrMore() *R {
	return Con(r, r.ZeroOrMore()).As(parens(r.Name) + "+")
}

func (r *R) ZeroOrMore() *R {
	x := newR().As(parens(r.Name) + "*")
	x.Alts = Or(Con(x, r), Null).toAlts(x)
	return x
}
func (r *R) toAlts(parent *R) Alts {
	r.eachAlt(func(a *Alt) {
		a.Parent = parent
	})
	return r.Alts
}

func parens(s string) string {
	if strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
		return s
	}
	return "(" + s + ")"
}
<commit_msg>change * from left to right recursion.<commit_after>package parse

import "strings"

//const noID = -42

var (
	EOF  = Term("EOF")
	Null = Term("Null")
	Self = Term("Self")
)

func newR() *R {
	return &R{}
}

func Term(name string) *R {
	return newR().As(name)
}

func Rule(name string, rules ...*R) *R {
	r := Con(rules...)
	r.initRecursiveRule(make(map[*R]bool), r)
	return r.As(name)
}
func (r *R) initRecursiveRule(m map[*R]bool, selfValue *R) {
	if m[r] {
		return
	}
	m[r] = true
	for _, alt := range r.Alts {
		if alt.Parent == Self {
			alt.Parent = selfValue
		}
		for i := range alt.Rules {
			if alt.Rules[i] == Self {
				alt.Rules[i] = selfValue
			} else {
				alt.Rules[i].initRecursiveRule(m, selfValue)
			}
		}
	}
}

func Con(rules ...*R) *R {
	if len(rules) == 1 {
		return rules[0]
	}
	r := newR().As(Rules(rules).expr(" "))
	r.Alts = Alts{{r, rules}}
	return r
}

func Or(rules ...*R) *R {
	if len(rules) == 1 {
		return rules[0]
	}
	r := newR().As("(" + Rules(rules).expr("|") + ")")
	r.Alts = make(Alts, len(rules))
	for i := range rules {
		r.Alts[i] = rules[i].toAlt(r)
	}
	return r
}
func (r *R) toAlt(parent *R) *Alt {
	if len(r.Alts) == 1 {
		return &Alt{parent, r.Alts[0].Rules}
	}
	return &Alt{parent, Rules{r}}
}

func (r *R) As(name string) *R {
	r.Name = name
	return r
}

func (r *R) ZeroOrOne() *R {
	return Or(r, Null).As(parens(r.Name) + "?")
}

func (r *R) OneOrMore() *R {
	return Con(r, r.ZeroOrMore()).As(parens(r.Name) + "+")
}

func (r *R) ZeroOrMore() *R {
	x := newR().As(parens(r.Name) + "*")
	x.Alts = Or(Con(r, x), Null).toAlts(x)
	return x
}
func (r *R) toAlts(parent *R) Alts {
	r.eachAlt(func(a *Alt) {
		a.Parent = parent
	})
	return r.Alts
}

func parens(s string) string {
	if strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
		return s
	}
	return "(" + s + ")"
}
<|endoftext|>


<commit_before>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

// NodeType identifies the type of a parse tree node.
type NodeType int

const (
	NodeSection NodeType = iota
	NodeParagraph
	NodeBlankLine
	NodeAdornment
)

var nodeTypes = [...]string{
	"NodeSection",
	"NodeParagraph",
	"NodeBlankLine",
	"NodeAdornment",
}

func (n NodeType) Type() NodeType {
	return n
}

func (n NodeType) String() string {
	return nodeTypes[n]
}

func (n NodeType) MarshalText() ([]byte, error) {
	return []byte(n.String()), nil
}

type Node interface {
	LineNumber() Line
	NodeType() NodeType
	Position() StartPosition
}

type NodeList []Node

func newList() *NodeList {
	return new(NodeList)
}

func (l *NodeList) append(n Node) {
	*l = append(*l, n)

}

type SectionNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Text          string   `json:"text"`
	Level         int      `json:"level"`
	Length        int      `json:"length"`
	StartPosition `json:"startPosition"`
	Line          `json:"line"`
	OverLine      *AdornmentNode `json:"overLine"`
	UnderLine     *AdornmentNode `json:"underLine"`
	NodeList      NodeList       `json:"nodeList"`
}

func (s *SectionNode) NodeType() NodeType {
	return s.Type
}

func newSection(title *item, overAdorn *item, underAdorn *item) *SectionNode {
	n := &SectionNode{
		Id:            title.Id,
		Type:          NodeSection,
		Text:          title.Text.(string),
		StartPosition: title.StartPosition,
		Length:        title.Length,
		Line:          title.Line,
	}

	if overAdorn != nil && overAdorn.Text != nil {
		Rune := rune(overAdorn.Text.(string)[0])
		n.OverLine = &AdornmentNode{
			Id:            title.Id,
			Type:          NodeAdornment,
			Rune:          Rune,
			StartPosition: overAdorn.StartPosition,
			Line:          overAdorn.Line,
			Length:        overAdorn.Length,
		}
	}

	Rune := rune(underAdorn.Text.(string)[0])
	n.UnderLine = &AdornmentNode{
		Id:            title.Id,
		Rune:          Rune,
		Type:          NodeAdornment,
		StartPosition: underAdorn.StartPosition,
		Line:          underAdorn.Line,
		Length:        underAdorn.Length,
	}

	return n
}

type AdornmentNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Rune          rune     `json:"rune"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func (a AdornmentNode) NodeType() NodeType {
	return a.Type
}

type BlankLineNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"nodetype"`
	Text          string   `json:"text"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func (b BlankLineNode) NodeType() NodeType {
	return b.Type
}

func newBlankLine(i *item) *BlankLineNode {
	return &BlankLineNode{
		Id:            i.Id,
		Type:          NodeBlankLine,
		Text:          i.Text.(string),
		Length:        i.Length,
		Line:          i.Line,
		StartPosition: i.StartPosition,
	}
}

type ParagraphNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Text          string   `json:"text"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func newParagraph(i *item) *ParagraphNode {
	return &ParagraphNode{
		Id:            i.Id,
		Type:          NodeParagraph,
		Text:          i.Text.(string),
		Length:        i.Length,
		Line:          i.Line,
		StartPosition: i.StartPosition,
	}
}

func (p ParagraphNode) NodeType() NodeType {
	return p.Type
}

type SpaceNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Text          string   `json:"text"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func newSpace(i *item) *SpaceNode {
	return &SpaceNode{
		Id:            i.Id,
		Type:          NodeParagraph,
		Text:          i.Text.(string),
		Length:        i.Length,
		Line:          i.Line,
		StartPosition: i.StartPosition,
	}
}

func (s SpaceNode) NodeType() NodeType {
	return s.Type
}
<commit_msg>node.go: Add type BlockQuoteNode<commit_after>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

// NodeType identifies the type of a parse tree node.
type NodeType int

const (
	NodeSection NodeType = iota
	NodeParagraph
	NodeBlankLine
	NodeAdornment
)

var nodeTypes = [...]string{
	"NodeSection",
	"NodeParagraph",
	"NodeBlankLine",
	"NodeAdornment",
}

func (n NodeType) Type() NodeType {
	return n
}

func (n NodeType) String() string {
	return nodeTypes[n]
}

func (n NodeType) MarshalText() ([]byte, error) {
	return []byte(n.String()), nil
}

type Node interface {
	LineNumber() Line
	NodeType() NodeType
	Position() StartPosition
}

type NodeList []Node

func newList() *NodeList {
	return new(NodeList)
}

func (l *NodeList) append(n Node) {
	*l = append(*l, n)

}

type SectionNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Text          string   `json:"text"`
	Level         int      `json:"level"`
	Length        int      `json:"length"`
	StartPosition `json:"startPosition"`
	Line          `json:"line"`
	OverLine      *AdornmentNode `json:"overLine"`
	UnderLine     *AdornmentNode `json:"underLine"`
	NodeList      NodeList       `json:"nodeList"`
}

func (s *SectionNode) NodeType() NodeType {
	return s.Type
}

func newSection(title *item, overAdorn *item, underAdorn *item) *SectionNode {
	n := &SectionNode{
		Id:            title.Id,
		Type:          NodeSection,
		Text:          title.Text.(string),
		StartPosition: title.StartPosition,
		Length:        title.Length,
		Line:          title.Line,
	}

	if overAdorn != nil && overAdorn.Text != nil {
		Rune := rune(overAdorn.Text.(string)[0])
		n.OverLine = &AdornmentNode{
			Id:            title.Id,
			Type:          NodeAdornment,
			Rune:          Rune,
			StartPosition: overAdorn.StartPosition,
			Line:          overAdorn.Line,
			Length:        overAdorn.Length,
		}
	}

	Rune := rune(underAdorn.Text.(string)[0])
	n.UnderLine = &AdornmentNode{
		Id:            title.Id,
		Rune:          Rune,
		Type:          NodeAdornment,
		StartPosition: underAdorn.StartPosition,
		Line:          underAdorn.Line,
		Length:        underAdorn.Length,
	}

	return n
}

type AdornmentNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Rune          rune     `json:"rune"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func (a AdornmentNode) NodeType() NodeType {
	return a.Type
}

type BlankLineNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"nodetype"`
	Text          string   `json:"text"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func (b BlankLineNode) NodeType() NodeType {
	return b.Type
}

func newBlankLine(i *item) *BlankLineNode {
	return &BlankLineNode{
		Id:            i.Id,
		Type:          NodeBlankLine,
		Text:          i.Text.(string),
		Length:        i.Length,
		Line:          i.Line,
		StartPosition: i.StartPosition,
	}
}

type ParagraphNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Text          string   `json:"text"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func newParagraph(i *item) *ParagraphNode {
	return &ParagraphNode{
		Id:            i.Id,
		Type:          NodeParagraph,
		Text:          i.Text.(string),
		Length:        i.Length,
		Line:          i.Line,
		StartPosition: i.StartPosition,
	}
}

func (p ParagraphNode) NodeType() NodeType {
	return p.Type
}

type SpaceNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Text          string   `json:"text"`
	Length        int      `json:"length"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
}

func newSpace(i *item) *SpaceNode {
	return &SpaceNode{
		Id:            i.Id,
		Type:          NodeParagraph,
		Text:          i.Text.(string),
		Length:        i.Length,
		Line:          i.Line,
		StartPosition: i.StartPosition,
	}
}

func (s SpaceNode) NodeType() NodeType {
	return s.Type
}

type BlockQuoteNode struct {
	Id            int      `json:"id"`
	Type          NodeType `json:"type"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
	NodeList      NodeList `json:"nodeList"`
}

func newBlockQuote(i *item) *BlockQuoteNode {
	return &BlockQuoteNode{
		Id:            i.Id,
		Type:          NodeParagraph,
		Line:          i.Line,
		StartPosition: i.StartPosition,
	}
}

func (b BlockQuoteNode) NodeType() NodeType {
	return b.Type
}
<|endoftext|>


<commit_before>package cluster

import (
	"sort"
	"sync"

	"github.com/Shopify/sarama"
)

type partitionConsumer struct {
	pcm sarama.PartitionConsumer

	state partitionState
	mutex sync.Mutex

	closed      bool
	dying, dead chan none
}

func newPartitionConsumer(manager sarama.Consumer, topic string, partition int32, info offsetInfo, defaultOffset int64) (*partitionConsumer, error) {
	pcm, err := manager.ConsumePartition(topic, partition, info.NextOffset(defaultOffset))

	// Resume from default offset, if requested offset is out-of-range
	if err == sarama.ErrOffsetOutOfRange {
		info.Offset = -1
		pcm, err = manager.ConsumePartition(topic, partition, defaultOffset)
	}
	if err != nil {
		return nil, err
	}

	return &partitionConsumer{
		pcm:   pcm,
		state: partitionState{Info: info},

		dying: make(chan none),
		dead:  make(chan none),
	}, nil
}

func (c *partitionConsumer) Loop(messages chan<- *sarama.ConsumerMessage, errors chan<- error) {
	for {
		select {
		case msg := <-c.pcm.Messages():
			if msg != nil {
				select {
				case messages <- msg:
				case <-c.dying:
					close(c.dead)
					return
				}
			}
		case err := <-c.pcm.Errors():
			if err != nil {
				select {
				case errors <- err:
				case <-c.dying:
					close(c.dead)
					return
				}
			}
		case <-c.dying:
			close(c.dead)
			return
		}
	}
}

func (c *partitionConsumer) Close() error {
	if c.closed {
		return nil
	}

	c.closed = true
	close(c.dying)
	<-c.dead

	return c.pcm.Close()
}

func (c *partitionConsumer) State() partitionState {
	if c == nil {
		return partitionState{}
	}

	c.mutex.Lock()
	state := c.state
	c.mutex.Unlock()

	return state
}

func (c *partitionConsumer) MarkCommitted(offset int64) {
	if c == nil {
		return
	}

	c.mutex.Lock()
	if offset == c.state.Info.Offset {
		c.state.Dirty = false
	}
	c.mutex.Unlock()
}

func (c *partitionConsumer) MarkOffset(offset int64, metadata string) {
	if c == nil {
		return
	}

	c.mutex.Lock()
	if offset > c.state.Info.Offset {
		c.state.Info.Offset = offset
		c.state.Info.Metadata = metadata
		c.state.Dirty = true
	}
	c.mutex.Unlock()
}

// --------------------------------------------------------------------

type partitionState struct {
	Info  offsetInfo
	Dirty bool
}

// --------------------------------------------------------------------

type partitionMap struct {
	data  map[topicPartition]*partitionConsumer
	mutex sync.RWMutex
}

func newPartitionMap() *partitionMap {
	return &partitionMap{
		data: make(map[topicPartition]*partitionConsumer),
	}
}

func (m *partitionMap) Fetch(topic string, partition int32) *partitionConsumer {
	m.mutex.RLock()
	pc, _ := m.data[topicPartition{topic, partition}]
	m.mutex.RUnlock()
	return pc
}

func (m *partitionMap) Store(topic string, partition int32, pc *partitionConsumer) {
	m.mutex.Lock()
	m.data[topicPartition{topic, partition}] = pc
	m.mutex.Unlock()
}

func (m *partitionMap) HasDirty() bool {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	for _, pc := range m.data {
		if state := pc.State(); state.Dirty {
			return true
		}
	}
	return false
}

func (m *partitionMap) Snapshot() map[topicPartition]partitionState {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	snap := make(map[topicPartition]partitionState, len(m.data))
	for tp, pc := range m.data {
		snap[tp] = pc.State()
	}
	return snap
}

func (m *partitionMap) Stop() {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	wg := new(sync.WaitGroup)
	for tp := range m.data {
		wg.Add(1)
		go func(p *partitionConsumer) {
			_ = p.Close()
			wg.Done()
		}(m.data[tp])
	}
	wg.Wait()
}

func (m *partitionMap) Clear() {
	m.mutex.Lock()
	for tp := range m.data {
		delete(m.data, tp)
	}
	m.mutex.Unlock()
}

func (m *partitionMap) Info() map[string][]int32 {
	info := make(map[string][]int32)
	m.mutex.RLock()
	for tp := range m.data {
		info[tp.Topic] = append(info[tp.Topic], tp.Partition)
	}
	m.mutex.RUnlock()

	for topic := range info {
		sort.Sort(int32Slice(info[topic]))
	}
	return info
}
<commit_msg>Safer close<commit_after>package cluster

import (
	"sort"
	"sync"

	"github.com/Shopify/sarama"
)

type partitionConsumer struct {
	pcm sarama.PartitionConsumer

	state partitionState
	mutex sync.Mutex

	closed      bool
	dying, dead chan none
}

func newPartitionConsumer(manager sarama.Consumer, topic string, partition int32, info offsetInfo, defaultOffset int64) (*partitionConsumer, error) {
	pcm, err := manager.ConsumePartition(topic, partition, info.NextOffset(defaultOffset))

	// Resume from default offset, if requested offset is out-of-range
	if err == sarama.ErrOffsetOutOfRange {
		info.Offset = -1
		pcm, err = manager.ConsumePartition(topic, partition, defaultOffset)
	}
	if err != nil {
		return nil, err
	}

	return &partitionConsumer{
		pcm:   pcm,
		state: partitionState{Info: info},

		dying: make(chan none),
		dead:  make(chan none),
	}, nil
}

func (c *partitionConsumer) Loop(messages chan<- *sarama.ConsumerMessage, errors chan<- error) {
	defer close(c.dead)

	for {
		select {
		case msg, ok := <-c.pcm.Messages():
			if !ok {
				return
			}
			select {
			case messages <- msg:
			case <-c.dying:
				return
			}
		case err, ok := <-c.pcm.Errors():
			if !ok {
				return
			}
			select {
			case errors <- err:
			case <-c.dying:
				return
			}
		case <-c.dying:
			return
		}
	}
}

func (c *partitionConsumer) Close() error {
	if c.closed {
		return nil
	}

	c.closed = true
	close(c.dying)
	<-c.dead

	return c.pcm.Close()
}

func (c *partitionConsumer) State() partitionState {
	if c == nil {
		return partitionState{}
	}

	c.mutex.Lock()
	state := c.state
	c.mutex.Unlock()

	return state
}

func (c *partitionConsumer) MarkCommitted(offset int64) {
	if c == nil {
		return
	}

	c.mutex.Lock()
	if offset == c.state.Info.Offset {
		c.state.Dirty = false
	}
	c.mutex.Unlock()
}

func (c *partitionConsumer) MarkOffset(offset int64, metadata string) {
	if c == nil {
		return
	}

	c.mutex.Lock()
	if offset > c.state.Info.Offset {
		c.state.Info.Offset = offset
		c.state.Info.Metadata = metadata
		c.state.Dirty = true
	}
	c.mutex.Unlock()
}

// --------------------------------------------------------------------

type partitionState struct {
	Info  offsetInfo
	Dirty bool
}

// --------------------------------------------------------------------

type partitionMap struct {
	data  map[topicPartition]*partitionConsumer
	mutex sync.RWMutex
}

func newPartitionMap() *partitionMap {
	return &partitionMap{
		data: make(map[topicPartition]*partitionConsumer),
	}
}

func (m *partitionMap) Fetch(topic string, partition int32) *partitionConsumer {
	m.mutex.RLock()
	pc, _ := m.data[topicPartition{topic, partition}]
	m.mutex.RUnlock()
	return pc
}

func (m *partitionMap) Store(topic string, partition int32, pc *partitionConsumer) {
	m.mutex.Lock()
	m.data[topicPartition{topic, partition}] = pc
	m.mutex.Unlock()
}

func (m *partitionMap) HasDirty() bool {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	for _, pc := range m.data {
		if state := pc.State(); state.Dirty {
			return true
		}
	}
	return false
}

func (m *partitionMap) Snapshot() map[topicPartition]partitionState {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	snap := make(map[topicPartition]partitionState, len(m.data))
	for tp, pc := range m.data {
		snap[tp] = pc.State()
	}
	return snap
}

func (m *partitionMap) Stop() {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	wg := new(sync.WaitGroup)
	for tp := range m.data {
		wg.Add(1)
		go func(p *partitionConsumer) {
			_ = p.Close()
			wg.Done()
		}(m.data[tp])
	}
	wg.Wait()
}

func (m *partitionMap) Clear() {
	m.mutex.Lock()
	for tp := range m.data {
		delete(m.data, tp)
	}
	m.mutex.Unlock()
}

func (m *partitionMap) Info() map[string][]int32 {
	info := make(map[string][]int32)
	m.mutex.RLock()
	for tp := range m.data {
		info[tp.Topic] = append(info[tp.Topic], tp.Partition)
	}
	m.mutex.RUnlock()

	for topic := range info {
		sort.Sort(int32Slice(info[topic]))
	}
	return info
}
<|endoftext|>


<commit_before>package git

import (
	"strings"
	"testing"
)

func TestPatch(t *testing.T) {
	repo := createTestRepo(t)
	defer repo.Free()
	//defer os.RemoveAll(repo.Workdir())

	_, originalTreeId := seedTestRepo(t, repo)
	originalTree, err := repo.LookupTree(originalTreeId)

	checkFatal(t, err)

	_, newTreeId := updateReadme(t, repo, "file changed\n")

	newTree, err := repo.LookupTree(newTreeId)
	checkFatal(t, err)

	diff, err := repo.DiffTreeToTree(originalTree, newTree, nil)
	checkFatal(t, err)

	patch, err := diff.Patch(0)
	checkFatal(t, err)

	patchStr, err := patch.String()
	checkFatal(t, err)
	if strings.Index(patchStr, "diff --git a/README b/README\nindex 257cc56..820734a 100644\n--- a/README\n+++ b/README\n@@ -1 +1 @@\n-foo\n+file changed") == -1 {
		t.Fatalf("patch was bad")
	}
}
<commit_msg>Fix test to force diff prefixes.<commit_after>package git

import (
	"strings"
	"testing"
)

func TestPatch(t *testing.T) {
	repo := createTestRepo(t)
	defer repo.Free()
	//defer os.RemoveAll(repo.Workdir())

	_, originalTreeId := seedTestRepo(t, repo)
	originalTree, err := repo.LookupTree(originalTreeId)

	checkFatal(t, err)

	_, newTreeId := updateReadme(t, repo, "file changed\n")

	newTree, err := repo.LookupTree(newTreeId)
	checkFatal(t, err)

	opts := &DiffOptions{
		OldPrefix: "a",
		NewPrefix: "b",
	}
	diff, err := repo.DiffTreeToTree(originalTree, newTree, opts)
	checkFatal(t, err)

	patch, err := diff.Patch(0)
	checkFatal(t, err)

	patchStr, err := patch.String()
	checkFatal(t, err)
	if strings.Index(patchStr, "diff --git a/README b/README\nindex 257cc56..820734a 100644\n--- a/README\n+++ b/README\n@@ -1 +1 @@\n-foo\n+file changed") == -1 {
		t.Fatalf("patch was bad")
	}
}
<|endoftext|>


<commit_before>// Copyright © 2013, S.Çağlar Onur
// Use of this source code is governed by a LGPLv2.1
// license that can be found in the LICENSE file.
//
// Authors:
// S.Çağlar Onur <caglar@10ur.org>

// +build linux

package lxc

// #include <lxc/lxc.h>
// #include <lxc/lxccontainer.h>
// #include "lxc.h"
import "C"

import (
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"
)

type Container struct {
	container *C.struct_lxc_container
	sync.RWMutex
}

// Returns container's name
func (lxc *Container) Name() string {
	lxc.RLock()
	defer lxc.RUnlock()
	return C.GoString(lxc.container.name)
}

// Returns whether the container is already defined or not
func (lxc *Container) Defined() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(C.lxc_container_defined(lxc.container))
}

// Returns whether the container is already running or not
func (lxc *Container) Running() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(C.lxc_container_running(lxc.container))
}

// Returns the container's state
func (lxc *Container) State() State {
	lxc.RLock()
	defer lxc.RUnlock()
	return stateMap[C.GoString(C.lxc_container_state(lxc.container))]
}

// Returns the container's PID
func (lxc *Container) InitPID() int {
	lxc.RLock()
	defer lxc.RUnlock()
	return int(C.lxc_container_init_pid(lxc.container))
}

// Returns whether the daemonize flag is set
func (lxc *Container) Daemonize() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(lxc.container.daemonize != 0)
}

// Sets the daemonize flag
func (lxc *Container) SetDaemonize() {
	lxc.Lock()
	defer lxc.Unlock()
	C.lxc_container_want_daemonize(lxc.container)
}

// Freezes the running container
func (lxc *Container) Freeze() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_freeze(lxc.container))
}

// Unfreezes the frozen container
func (lxc *Container) Unfreeze() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_unfreeze(lxc.container))
}

// Creates the container using given template and arguments
func (lxc *Container) Create(template string, args []string) bool {
	lxc.Lock()
	defer lxc.Unlock()

	ctemplate := C.CString(template)
	defer C.free(unsafe.Pointer(ctemplate))
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_create(lxc.container, ctemplate, &cargs[0]))
	}
	return bool(C.lxc_container_create(lxc.container, ctemplate, nil))
}

// Starts the container
func (lxc *Container) Start(useinit bool, args []string) bool {
	lxc.Lock()
	defer lxc.Unlock()

	cuseinit := 0
	if useinit {
		cuseinit = 1
	}
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), &cargs[0]))
	}
	return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), nil))
}

// Stops the container
func (lxc *Container) Stop() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_stop(lxc.container))
}

// Shutdowns the container
func (lxc *Container) Shutdown(timeout int) bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_shutdown(lxc.container, C.int(timeout)))
}

// Destroys the container
func (lxc *Container) Destroy() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_destroy(lxc.container))
}

// Waits till the container changes its state or timeouts
func (lxc *Container) Wait(state State, timeout int) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cstate := C.CString(state.String())
	defer C.free(unsafe.Pointer(cstate))
	return bool(C.lxc_container_wait(lxc.container, cstate, C.int(timeout)))
}

// Returns the container's configuration file's name
func (lxc *Container) ConfigFileName() string {
	lxc.RLock()
	defer lxc.RUnlock()
	return C.GoString(C.lxc_container_config_file_name(lxc.container))
}

// Returns the value of the given key
func (lxc *Container) ConfigItem(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	ret := strings.TrimSpace(C.GoString(C.lxc_container_get_config_item(lxc.container, ckey)))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetConfigItem(key string, value string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_config_item(lxc.container, ckey, cvalue))
}

// Returns the value of the given key
func (lxc *Container) CgroupItem(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	ret := strings.TrimSpace(C.GoString(C.lxc_container_get_cgroup_item(lxc.container, ckey)))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetCgroupItem(key string, value string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_cgroup_item(lxc.container, ckey, cvalue))
}

// Clears the value of given key
func (lxc *Container) ClearConfigItem(key string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	return bool(C.lxc_container_clear_config_item(lxc.container, ckey))
}

// Returns the keys
func (lxc *Container) Keys(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	ret := strings.TrimSpace(C.GoString(C.lxc_container_get_keys(lxc.container, ckey)))
	return strings.Split(ret, "\n")
}

// Loads the configuration file from given path
func (lxc *Container) LoadConfigFile(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_load_config(lxc.container, cpath))
}

// Saves the configuration file to given path
func (lxc *Container) SaveConfigFile(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_save_config(lxc.container, cpath))
}

// Returns the configuration file's path
func (lxc *Container) ConfigPath() string {
	lxc.RLock()
	defer lxc.RUnlock()
	return C.GoString(C.lxc_container_get_config_path(lxc.container))
}

// Sets the configuration file's path
func (lxc *Container) SetConfigPath(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_set_config_path(lxc.container, cpath))
}

func (lxc *Container) NumberOfNetworkInterfaces() int {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		return len(lxc.ConfigItem("lxc.network"))
	}
	return -1
}

func (lxc *Container) MemoryUsageInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		memUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memUsed), err
	}
	return -1, nil
}

func (lxc *Container) SwapUsageInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		swapUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapUsed), err
	}
	return -1, nil
}

func (lxc *Container) MemoryLimitInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		memLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memLimit), err
	}
	return -1, nil
}

func (lxc *Container) SwapLimitInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		swapLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapLimit), err
	}
	return -1, nil
}

// Returns the total CPU time (in nanoseconds) consumed by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTime() (time.Duration, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		cpuUsage, err := strconv.ParseInt(lxc.CgroupItem("cpuacct.usage")[0], 10, 64)
		if err != nil {
			return -1, err
		}
		return time.Duration(cpuUsage), err
	}
	return -1, nil
}

// Returns the CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTimePerCPU() ([]time.Duration, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	var cpuTimes []time.Duration

	if lxc.Running() {
		for _, v := range strings.Split(lxc.CgroupItem("cpuacct.usage_percpu")[0], " ") {
			cpuUsage, err := strconv.ParseInt(v, 10, 64)
			if err != nil {
				return nil, err
			}
			cpuTimes = append(cpuTimes, time.Duration(cpuUsage))
		}
		return cpuTimes, nil
	}
	return nil, nil
}

// Returns the number of CPU cycles (in the units defined by USER_HZ on the system) consumed by tasks in this cgroup and its children in both user mode and system (kernel) mode.
func (lxc *Container) CPUStats() ([]int64, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		cpuStat := lxc.CgroupItem("cpuacct.stat")
		user, _ := strconv.ParseInt(strings.Split(cpuStat[0], " ")[1], 10, 64)
		system, _ := strconv.ParseInt(strings.Split(cpuStat[1], " ")[1], 10, 64)
		return []int64{user, system}, nil
	}
	return nil, nil
}
<commit_msg>backport couple of fixes from devel<commit_after>// Copyright © 2013, S.Çağlar Onur
// Use of this source code is governed by a LGPLv2.1
// license that can be found in the LICENSE file.
//
// Authors:
// S.Çağlar Onur <caglar@10ur.org>

// +build linux

package lxc

// #include <lxc/lxc.h>
// #include <lxc/lxccontainer.h>
// #include "lxc.h"
import "C"

import (
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"
)

type Container struct {
	container *C.struct_lxc_container
	sync.RWMutex
}

// Returns container's name
func (lxc *Container) Name() string {
	lxc.RLock()
	defer lxc.RUnlock()
	return C.GoString(lxc.container.name)
}

// Returns whether the container is already defined or not
func (lxc *Container) Defined() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(C.lxc_container_defined(lxc.container))
}

// Returns whether the container is already running or not
func (lxc *Container) Running() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(C.lxc_container_running(lxc.container))
}

// Returns the container's state
func (lxc *Container) State() State {
	lxc.RLock()
	defer lxc.RUnlock()
	return stateMap[C.GoString(C.lxc_container_state(lxc.container))]
}

// Returns the container's PID
func (lxc *Container) InitPID() int {
	lxc.RLock()
	defer lxc.RUnlock()
	return int(C.lxc_container_init_pid(lxc.container))
}

// Returns whether the daemonize flag is set
func (lxc *Container) Daemonize() bool {
	lxc.RLock()
	defer lxc.RUnlock()
	return bool(lxc.container.daemonize != 0)
}

// Sets the daemonize flag
func (lxc *Container) SetDaemonize() {
	lxc.Lock()
	defer lxc.Unlock()
	C.lxc_container_want_daemonize(lxc.container)
}

// Freezes the running container
func (lxc *Container) Freeze() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_freeze(lxc.container))
}

// Unfreezes the frozen container
func (lxc *Container) Unfreeze() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_unfreeze(lxc.container))
}

// Creates the container using given template and arguments
func (lxc *Container) Create(template string, args []string) bool {
	lxc.Lock()
	defer lxc.Unlock()

	ctemplate := C.CString(template)
	defer C.free(unsafe.Pointer(ctemplate))
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_create(lxc.container, ctemplate, &cargs[0]))
	}
	return bool(C.lxc_container_create(lxc.container, ctemplate, nil))
}

// Starts the container
func (lxc *Container) Start(useinit bool, args []string) bool {
	lxc.Lock()
	defer lxc.Unlock()

	cuseinit := 0
	if useinit {
		cuseinit = 1
	}
	if args != nil {
		cargs := makeArgs(args)
		defer freeArgs(cargs)
		return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), &cargs[0]))
	}
	return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), nil))
}

// Stops the container
func (lxc *Container) Stop() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_stop(lxc.container))
}

// Shutdowns the container
func (lxc *Container) Shutdown(timeout int) bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_shutdown(lxc.container, C.int(timeout)))
}

// Destroys the container
func (lxc *Container) Destroy() bool {
	lxc.Lock()
	defer lxc.Unlock()
	return bool(C.lxc_container_destroy(lxc.container))
}

// Waits till the container changes its state or timeouts
func (lxc *Container) Wait(state State, timeout int) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cstate := C.CString(state.String())
	defer C.free(unsafe.Pointer(cstate))
	return bool(C.lxc_container_wait(lxc.container, cstate, C.int(timeout)))
}

// Returns the container's configuration file's name
func (lxc *Container) ConfigFileName() string {
	lxc.RLock()
	defer lxc.RUnlock()

	// allocated in lxc.c
	configFileName := C.lxc_container_config_file_name(lxc.container)
	defer C.free(unsafe.Pointer(configFileName))

	return C.GoString(configFileName)
}

// Returns the value of the given key
func (lxc *Container) ConfigItem(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	configItem := C.lxc_container_get_config_item(lxc.container, ckey)
	defer C.free(unsafe.Pointer(configItem))

	ret := strings.TrimSpace(C.GoString(configItem))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetConfigItem(key string, value string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_config_item(lxc.container, ckey, cvalue))
}

// Returns the value of the given key
func (lxc *Container) CgroupItem(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	cgroupItem := C.lxc_container_get_cgroup_item(lxc.container, ckey)
	defer C.free(unsafe.Pointer(cgroupItem))

	ret := strings.TrimSpace(C.GoString(cgroupItem))
	return strings.Split(ret, "\n")
}

// Sets the value of given key
func (lxc *Container) SetCgroupItem(key string, value string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	return bool(C.lxc_container_set_cgroup_item(lxc.container, ckey, cvalue))
}

// Clears the value of given key
func (lxc *Container) ClearConfigItem(key string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))
	return bool(C.lxc_container_clear_config_item(lxc.container, ckey))
}

// Returns the keys
func (lxc *Container) Keys(key string) []string {
	lxc.RLock()
	defer lxc.RUnlock()

	ckey := C.CString(key)
	defer C.free(unsafe.Pointer(ckey))

	// allocated in lxc.c
	keys := C.lxc_container_get_keys(lxc.container, ckey)
	defer C.free(unsafe.Pointer(keys))

	ret := strings.TrimSpace(C.GoString(keys))
	return strings.Split(ret, "\n")
}

// Loads the configuration file from given path
func (lxc *Container) LoadConfigFile(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_load_config(lxc.container, cpath))
}

// Saves the configuration file to given path
func (lxc *Container) SaveConfigFile(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_save_config(lxc.container, cpath))
}

// Returns the configuration file's path
func (lxc *Container) ConfigPath() string {
	lxc.RLock()
	defer lxc.RUnlock()
	return C.GoString(C.lxc_container_get_config_path(lxc.container))
}

// Sets the configuration file's path
func (lxc *Container) SetConfigPath(path string) bool {
	lxc.Lock()
	defer lxc.Unlock()
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	return bool(C.lxc_container_set_config_path(lxc.container, cpath))
}

func (lxc *Container) NumberOfNetworkInterfaces() int {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		return len(lxc.ConfigItem("lxc.network"))
	}
	return -1
}

func (lxc *Container) MemoryUsageInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		memUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memUsed), err
	}
	return -1, nil
}

func (lxc *Container) SwapUsageInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		swapUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapUsed), err
	}
	return -1, nil
}

func (lxc *Container) MemoryLimitInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		memLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(memLimit), err
	}
	return -1, nil
}

func (lxc *Container) SwapLimitInBytes() (ByteSize, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		swapLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
		if err != nil {
			return -1, err
		}
		return ByteSize(swapLimit), err
	}
	return -1, nil
}

// Returns the total CPU time (in nanoseconds) consumed by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTime() (time.Duration, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		cpuUsage, err := strconv.ParseInt(lxc.CgroupItem("cpuacct.usage")[0], 10, 64)
		if err != nil {
			return -1, err
		}
		return time.Duration(cpuUsage), err
	}
	return -1, nil
}

// Returns the CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTimePerCPU() ([]time.Duration, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	var cpuTimes []time.Duration

	if lxc.Running() {
		for _, v := range strings.Split(lxc.CgroupItem("cpuacct.usage_percpu")[0], " ") {
			cpuUsage, err := strconv.ParseInt(v, 10, 64)
			if err != nil {
				return nil, err
			}
			cpuTimes = append(cpuTimes, time.Duration(cpuUsage))
		}
		return cpuTimes, nil
	}
	return nil, nil
}

// Returns the number of CPU cycles (in the units defined by USER_HZ on the system) consumed by tasks in this cgroup and its children in both user mode and system (kernel) mode.
func (lxc *Container) CPUStats() ([]int64, error) {
	lxc.RLock()
	defer lxc.RUnlock()
	if lxc.Running() {
		cpuStat := lxc.CgroupItem("cpuacct.stat")
		user, _ := strconv.ParseInt(strings.Split(cpuStat[0], " ")[1], 10, 64)
		system, _ := strconv.ParseInt(strings.Split(cpuStat[1], " ")[1], 10, 64)
		return []int64{user, system}, nil
	}
	return nil, nil
}
<|endoftext|>


<commit_before>package gopikacloud

// ContainerConfig definition
type ContainerConfig struct {
	ID         string `json:"id"`
	Name       string `json:"name"`
	Image      string `json:"image"`
	AlwaysPull bool   `json:"always_pull"`
}
<commit_msg>add new stream_logs attribute<commit_after>package gopikacloud

// ContainerConfig definition
type ContainerConfig struct {
	ID         string `json:"id"`
	Name       string `json:"name"`
	Image      string `json:"image"`
	AlwaysPull bool   `json:"always_pull"`
	StreamLogs bool   `json:"stream_logs"`
}
<|endoftext|>


<commit_before>// +build coprocess

//go:generate msgp
//msgp:ignore CoProcessor CoProcessMiddleware CoProcessMiddlewareConfig TykMiddleware

package main

import (
	"github.com/Sirupsen/logrus"
	"github.com/gorilla/context"
	"github.com/mitchellh/mapstructure"

	"github.com/TykTechnologies/tyk/coprocess"
	"github.com/TykTechnologies/tykcommon"

	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
)

// EnableCoProcess will be overridden by config.EnableCoProcess.
var EnableCoProcess = false

// GlobalDispatcher will be implemented by the current CoProcess driver.
var GlobalDispatcher coprocess.Dispatcher

// CoProcessMiddleware is the basic CP middleware struct.
type CoProcessMiddleware struct {
	*TykMiddleware
	HookType         coprocess.HookType
	HookName         string
	MiddlewareDriver tykcommon.MiddlewareDriver
}

// CreateCoProcessMiddleware initializes a new CP middleware, takes hook type (pre, post, etc.), hook name ("my_hook") and driver ("python").
func CreateCoProcessMiddleware(hookName string, hookType coprocess.HookType, mwDriver tykcommon.MiddlewareDriver, tykMwSuper *TykMiddleware) func(http.Handler) http.Handler {
	dMiddleware := &CoProcessMiddleware{
		TykMiddleware:    tykMwSuper,
		HookType:         hookType,
		HookName:         hookName,
		MiddlewareDriver: mwDriver,
	}

	return CreateMiddleware(dMiddleware, tykMwSuper)
}

func doCoprocessReload() {
	log.WithFields(logrus.Fields{
		"prefix": "coprocess",
	}).Info("Reloading middlewares")
	GlobalDispatcher.Reload()

}

// CoProcessor represents a CoProcess during the request.
type CoProcessor struct {
	HookType   coprocess.HookType
	Middleware *CoProcessMiddleware
}

// GetObjectFromRequest constructs a CoProcessObject from a given http.Request.
func (c *CoProcessor) GetObjectFromRequest(r *http.Request) *coprocess.Object {

	defer r.Body.Close()
	originalBody, _ := ioutil.ReadAll(r.Body)

	var object *coprocess.Object
	var miniRequestObject *coprocess.MiniRequestObject

	miniRequestObject = &coprocess.MiniRequestObject{
		Headers:         ProtoMap(r.Header),
		SetHeaders:      make(map[string]string, 0),
		DeleteHeaders:   make([]string, 0),
		Body:            string(originalBody),
		Url:             r.URL.Path,
		Params:          ProtoMap(r.URL.Query()),
		AddParams:       make(map[string]string),
		ExtendedParams:  ProtoMap(nil),
		DeleteParams:    make([]string, 0),
		ReturnOverrides: &coprocess.ReturnOverrides{-1, ""},
	}

	object = &coprocess.Object{
		Request:  miniRequestObject,
		HookName: c.Middleware.HookName,
	}

	// If a middleware is set, take its HookType, otherwise override it with CoProcessor.HookType
	if c.Middleware != nil && c.HookType == 0 {
		c.HookType = c.Middleware.HookType
	}

	object.HookType = c.HookType

	object.Metadata = make(map[string]string, 0)
	object.Spec = make(map[string]string, 0)

	// object.Session = SessionState{}

	// Append spec data:
	if c.Middleware != nil {
		object.Spec = map[string]string{
			"OrgID": c.Middleware.TykMiddleware.Spec.OrgID,
			"APIID": c.Middleware.TykMiddleware.Spec.APIID,
		}
	}

	// Encode the session object (if not a pre-process & not a custom key check):
	if c.HookType != coprocess.HookType_Pre && c.HookType != coprocess.HookType_CustomKeyCheck {
		var session interface{}
		session = context.Get(r, SessionData)
		if session != nil {
			sessionState := session.(SessionState)
			object.Session = ProtoSessionState(sessionState)
		}
	}

	return object
}

// ObjectPostProcess does CoProcessObject post-processing (adding/removing headers or params, etc.).
func (c *CoProcessor) ObjectPostProcess(object *coprocess.Object, r *http.Request) {
	r.ContentLength = int64(len(object.Request.Body))
	r.Body = ioutil.NopCloser(bytes.NewBufferString(object.Request.Body))

	for _, dh := range object.Request.DeleteHeaders {
		r.Header.Del(dh)
	}

	for h, v := range object.Request.SetHeaders {
		r.Header.Set(h, v)
	}

	values := r.URL.Query()
	for _, k := range object.Request.DeleteParams {
		values.Del(k)
	}

	for p, v := range object.Request.AddParams {
		values.Set(p, v)
	}

	r.URL.RawQuery = values.Encode()
}

// CoProcessInit creates a new CoProcessDispatcher, it will be called when Tyk starts.
func CoProcessInit() (err error) {
	if config.CoProcessOptions.EnableCoProcess {
		GlobalDispatcher, err = NewCoProcessDispatcher()
		EnableCoProcess = true
	}
	return err
}

// CoProcessMiddlewareConfig holds the middleware configuration.
type CoProcessMiddlewareConfig struct {
	ConfigData map[string]string `mapstructure:"config_data" bson:"config_data" json:"config_data"`
}

// New lets you do any initialisations for the object can be done here
func (m *CoProcessMiddleware) New() {}

// GetConfig retrieves the configuration from the API config - we user mapstructure for this for simplicity
func (m *CoProcessMiddleware) GetConfig() (interface{}, error) {
	var thisModuleConfig CoProcessMiddlewareConfig

	err := mapstructure.Decode(m.TykMiddleware.Spec.APIDefinition.RawData, &thisModuleConfig)
	if err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "jsvm",
		}).Error(err)
		return nil, err
	}

	return thisModuleConfig, nil
}

// IsEnabledForSpec checks if this middleware should be enabled for a given API.
func (m *CoProcessMiddleware) IsEnabledForSpec() bool {
	// This flag is true when Tyk has been compiled with CP support and when the configuration enables it.
	var enableCoProcess bool = config.CoProcessOptions.EnableCoProcess && EnableCoProcess
	// This flag indicates if the current spec specifies any CP custom middleware.
	var usesCoProcessMiddleware bool

	var supportedDrivers = []tykcommon.MiddlewareDriver{tykcommon.PythonDriver, tykcommon.LuaDriver, tykcommon.GrpcDriver}

	for _, driver := range supportedDrivers {
		if m.TykMiddleware.Spec.CustomMiddleware.Driver == driver && CoProcessName == string(driver) {
			usesCoProcessMiddleware = true
			break
		}
	}

	if usesCoProcessMiddleware && enableCoProcess {
		log.WithFields(logrus.Fields{
			"prefix": "coprocess",
		}).Debug("Enabling CP middleware.")
		return true
	}

	if usesCoProcessMiddleware && !enableCoProcess {
		log.WithFields(logrus.Fields{
			"prefix": "coprocess",
		}).Error("Your API specifies a CP custom middleware, either Tyk wasn't build with CP support or CP is not enabled in your Tyk configuration file!")
	}

	if !usesCoProcessMiddleware && m.TykMiddleware.Spec.CustomMiddleware.Driver != "" {
		log.WithFields(logrus.Fields{
			"prefix": "coprocess",
		}).Error("CP Driver not supported: ", m.TykMiddleware.Spec.CustomMiddleware.Driver)
	}

	return false
}

// ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *CoProcessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, configuration interface{}) (error, int) {
	log.WithFields(logrus.Fields{
		"prefix": "coprocess",
	}).Debug("CoProcess Request, HookType: ", m.HookType)

	if !EnableCoProcess {
		return nil, 200
	}

	var thisExtractor IdExtractor
	if m.TykMiddleware.Spec.EnableCoProcessAuth && m.TykMiddleware.Spec.CustomMiddleware.IdExtractor.Extractor != nil {
		log.Println("Using extractor, defining thisExtractor")
		thisExtractor = m.TykMiddleware.Spec.CustomMiddleware.IdExtractor.Extractor.(IdExtractor)
	}

	var returnOverrides ReturnOverrides
	var SessionID string

	if m.HookType == coprocess.HookType_CustomKeyCheck && thisExtractor != nil {
		SessionID, returnOverrides = thisExtractor.ExtractAndCheck(r)

		if returnOverrides.ResponseCode != 0 {
			if returnOverrides.ResponseError == "" {
				return nil, returnOverrides.ResponseCode
			} else {
				err := errors.New(returnOverrides.ResponseError)
				return err, returnOverrides.ResponseCode
			}
		}
	}

	// It's also possible to override the HookType:
	thisCoProcessor := CoProcessor{
		Middleware: m,
		// HookType: coprocess.PreHook,
	}

	object := thisCoProcessor.GetObjectFromRequest(r)

	returnObject := thisCoProcessor.Dispatch(object)

	thisCoProcessor.ObjectPostProcess(returnObject, r)

	authHeaderValue := returnObject.Metadata["token"]

	if returnObject.Request.ReturnOverrides.ResponseCode > 400 {

		log.WithFields(logrus.Fields{
			"path":   r.URL.Path,
			"origin": GetIPFromRequest(r),
			"key":    authHeaderValue,
		}).Info("Attempted access with invalid key.")

		// Fire Authfailed Event
		AuthFailed(m.TykMiddleware, r, authHeaderValue)

		// Report in health check
		ReportHealthCheckValue(m.Spec.Health, KeyFailure, "1")

		return errors.New("Key not authorised"), int(returnObject.Request.ReturnOverrides.ResponseCode)
	}

	if m.HookType == coprocess.HookType_CustomKeyCheck && thisExtractor != nil {
		if returnObject.Session == nil {
			return errors.New("Key not authorised"), 403
		} else {
			returnedSessionState := TykSessionState(returnObject.Session)
			thisExtractor.PostProcess(r, returnedSessionState, SessionID)
			return nil, 200
		}
	}

	return nil, 200
}
<commit_msg>Updating CP auth logic.<commit_after>// +build coprocess

//go:generate msgp
//msgp:ignore CoProcessor CoProcessMiddleware CoProcessMiddlewareConfig TykMiddleware

package main

import (
	"github.com/Sirupsen/logrus"
	"github.com/gorilla/context"
	"github.com/mitchellh/mapstructure"

	"github.com/TykTechnologies/tyk/coprocess"
	"github.com/TykTechnologies/tykcommon"

	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
)

// EnableCoProcess will be overridden by config.EnableCoProcess.
var EnableCoProcess = false

// GlobalDispatcher will be implemented by the current CoProcess driver.
var GlobalDispatcher coprocess.Dispatcher

// CoProcessMiddleware is the basic CP middleware struct.
type CoProcessMiddleware struct {
	*TykMiddleware
	HookType         coprocess.HookType
	HookName         string
	MiddlewareDriver tykcommon.MiddlewareDriver
}

// CreateCoProcessMiddleware initializes a new CP middleware, takes hook type (pre, post, etc.), hook name ("my_hook") and driver ("python").
func CreateCoProcessMiddleware(hookName string, hookType coprocess.HookType, mwDriver tykcommon.MiddlewareDriver, tykMwSuper *TykMiddleware) func(http.Handler) http.Handler {
	dMiddleware := &CoProcessMiddleware{
		TykMiddleware:    tykMwSuper,
		HookType:         hookType,
		HookName:         hookName,
		MiddlewareDriver: mwDriver,
	}

	return CreateMiddleware(dMiddleware, tykMwSuper)
}

func doCoprocessReload() {
	log.WithFields(logrus.Fields{
		"prefix": "coprocess",
	}).Info("Reloading middlewares")
	GlobalDispatcher.Reload()

}

// CoProcessor represents a CoProcess during the request.
type CoProcessor struct {
	HookType   coprocess.HookType
	Middleware *CoProcessMiddleware
}

// GetObjectFromRequest constructs a CoProcessObject from a given http.Request.
func (c *CoProcessor) GetObjectFromRequest(r *http.Request) *coprocess.Object {

	defer r.Body.Close()
	originalBody, _ := ioutil.ReadAll(r.Body)

	var object *coprocess.Object
	var miniRequestObject *coprocess.MiniRequestObject

	miniRequestObject = &coprocess.MiniRequestObject{
		Headers:         ProtoMap(r.Header),
		SetHeaders:      make(map[string]string, 0),
		DeleteHeaders:   make([]string, 0),
		Body:            string(originalBody),
		Url:             r.URL.Path,
		Params:          ProtoMap(r.URL.Query()),
		AddParams:       make(map[string]string),
		ExtendedParams:  ProtoMap(nil),
		DeleteParams:    make([]string, 0),
		ReturnOverrides: &coprocess.ReturnOverrides{-1, ""},
	}

	object = &coprocess.Object{
		Request:  miniRequestObject,
		HookName: c.Middleware.HookName,
	}

	// If a middleware is set, take its HookType, otherwise override it with CoProcessor.HookType
	if c.Middleware != nil && c.HookType == 0 {
		c.HookType = c.Middleware.HookType
	}

	object.HookType = c.HookType

	object.Metadata = make(map[string]string, 0)
	object.Spec = make(map[string]string, 0)

	// object.Session = SessionState{}

	// Append spec data:
	if c.Middleware != nil {
		object.Spec = map[string]string{
			"OrgID": c.Middleware.TykMiddleware.Spec.OrgID,
			"APIID": c.Middleware.TykMiddleware.Spec.APIID,
		}
	}

	// Encode the session object (if not a pre-process & not a custom key check):
	if c.HookType != coprocess.HookType_Pre && c.HookType != coprocess.HookType_CustomKeyCheck {
		var session interface{}
		session = context.Get(r, SessionData)
		if session != nil {
			sessionState := session.(SessionState)
			object.Session = ProtoSessionState(sessionState)
		}
	}

	return object
}

// ObjectPostProcess does CoProcessObject post-processing (adding/removing headers or params, etc.).
func (c *CoProcessor) ObjectPostProcess(object *coprocess.Object, r *http.Request) {
	r.ContentLength = int64(len(object.Request.Body))
	r.Body = ioutil.NopCloser(bytes.NewBufferString(object.Request.Body))

	for _, dh := range object.Request.DeleteHeaders {
		r.Header.Del(dh)
	}

	for h, v := range object.Request.SetHeaders {
		r.Header.Set(h, v)
	}

	values := r.URL.Query()
	for _, k := range object.Request.DeleteParams {
		values.Del(k)
	}

	for p, v := range object.Request.AddParams {
		values.Set(p, v)
	}

	r.URL.RawQuery = values.Encode()
}

// CoProcessInit creates a new CoProcessDispatcher, it will be called when Tyk starts.
func CoProcessInit() (err error) {
	if config.CoProcessOptions.EnableCoProcess {
		GlobalDispatcher, err = NewCoProcessDispatcher()
		EnableCoProcess = true
	}
	return err
}

// CoProcessMiddlewareConfig holds the middleware configuration.
type CoProcessMiddlewareConfig struct {
	ConfigData map[string]string `mapstructure:"config_data" bson:"config_data" json:"config_data"`
}

// New lets you do any initialisations for the object can be done here
func (m *CoProcessMiddleware) New() {}

// GetConfig retrieves the configuration from the API config - we user mapstructure for this for simplicity
func (m *CoProcessMiddleware) GetConfig() (interface{}, error) {
	var thisModuleConfig CoProcessMiddlewareConfig

	err := mapstructure.Decode(m.TykMiddleware.Spec.APIDefinition.RawData, &thisModuleConfig)
	if err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "jsvm",
		}).Error(err)
		return nil, err
	}

	return thisModuleConfig, nil
}

// IsEnabledForSpec checks if this middleware should be enabled for a given API.
func (m *CoProcessMiddleware) IsEnabledForSpec() bool {
	// This flag is true when Tyk has been compiled with CP support and when the configuration enables it.
	var enableCoProcess bool = config.CoProcessOptions.EnableCoProcess && EnableCoProcess
	// This flag indicates if the current spec specifies any CP custom middleware.
	var usesCoProcessMiddleware bool

	var supportedDrivers = []tykcommon.MiddlewareDriver{tykcommon.PythonDriver, tykcommon.LuaDriver, tykcommon.GrpcDriver}

	for _, driver := range supportedDrivers {
		if m.TykMiddleware.Spec.CustomMiddleware.Driver == driver && CoProcessName == string(driver) {
			usesCoProcessMiddleware = true
			break
		}
	}

	if usesCoProcessMiddleware && enableCoProcess {
		log.WithFields(logrus.Fields{
			"prefix": "coprocess",
		}).Debug("Enabling CP middleware.")
		return true
	}

	if usesCoProcessMiddleware && !enableCoProcess {
		log.WithFields(logrus.Fields{
			"prefix": "coprocess",
		}).Error("Your API specifies a CP custom middleware, either Tyk wasn't build with CP support or CP is not enabled in your Tyk configuration file!")
	}

	if !usesCoProcessMiddleware && m.TykMiddleware.Spec.CustomMiddleware.Driver != "" {
		log.WithFields(logrus.Fields{
			"prefix": "coprocess",
		}).Error("CP Driver not supported: ", m.TykMiddleware.Spec.CustomMiddleware.Driver)
	}

	return false
}

// ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (m *CoProcessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, configuration interface{}) (error, int) {
	log.WithFields(logrus.Fields{
		"prefix": "coprocess",
	}).Debug("CoProcess Request, HookType: ", m.HookType)

	if !EnableCoProcess {
		return nil, 200
	}

	var thisExtractor IdExtractor
	if m.TykMiddleware.Spec.EnableCoProcessAuth && m.TykMiddleware.Spec.CustomMiddleware.IdExtractor.Extractor != nil {
		log.Println("Using extractor, defining thisExtractor")
		thisExtractor = m.TykMiddleware.Spec.CustomMiddleware.IdExtractor.Extractor.(IdExtractor)
	}

	var returnOverrides ReturnOverrides
	var SessionID string

	if m.HookType == coprocess.HookType_CustomKeyCheck && thisExtractor != nil {
		SessionID, returnOverrides = thisExtractor.ExtractAndCheck(r)

		if returnOverrides.ResponseCode != 0 {
			if returnOverrides.ResponseError == "" {
				return nil, returnOverrides.ResponseCode
			} else {
				err := errors.New(returnOverrides.ResponseError)
				return err, returnOverrides.ResponseCode
			}
		}
	}

	// It's also possible to override the HookType:
	thisCoProcessor := CoProcessor{
		Middleware: m,
		// HookType: coprocess.PreHook,
	}

	object := thisCoProcessor.GetObjectFromRequest(r)

	returnObject := thisCoProcessor.Dispatch(object)

	thisCoProcessor.ObjectPostProcess(returnObject, r)

	authHeaderValue := returnObject.Metadata["token"]

	// The CP middleware indicates this is a bad auth:
	if returnObject.Request.ReturnOverrides.ResponseCode > 400 {

		log.WithFields(logrus.Fields{
			"path":   r.URL.Path,
			"origin": GetIPFromRequest(r),
			"key":    authHeaderValue,
		}).Info("Attempted access with invalid key.")

		// Fire Authfailed Event
		AuthFailed(m.TykMiddleware, r, authHeaderValue)

		// Report in health check
		ReportHealthCheckValue(m.Spec.Health, KeyFailure, "1")

		return errors.New("Key not authorised"), int(returnObject.Request.ReturnOverrides.ResponseCode)
	}

	// The CP middleware didn't setup a session:
	if m.HookType == coprocess.HookType_CustomKeyCheck && returnObject.Session == nil {
		return errors.New("Key not authorised"), 403
	}

	// The CP middleware did setup a session, and we should pass it to the ID extractor (and cache it):
	if thisExtractor != nil {
		returnedSessionState := TykSessionState(returnObject.Session)
		thisExtractor.PostProcess(r, returnedSessionState, SessionID)
		return nil, 200
	}

	return nil, 200
}
<|endoftext|>