<commit_before>package ring

import (
	"encoding/binary"
	"fmt"
	"log"
	"math"
	"net"
	"sync"
	"time"
)

type ringConn struct {
	addr       string
	conn       net.Conn
	reader     *timeoutReader
	writerLock sync.Mutex
	writer     *timeoutWriter
}

// This is used as a placeholder in TCPMsgring.conns to indicate a goroutine is
// currently working on a connection with that address.
var _WORKING = &ringConn{}

// These are constants for use with TCPMsgRing.state
const (
	_STOPPED = iota
	_RUNNING
	_STOPPING
)

type TCPMsgRing struct {
	// addressIndex is the index given to a Node's Address method to determine
	// the network address to connect to (see Node's Address method for more
	// information).
	addressIndex        int
	chunkSize           int
	connectionTimeout   time.Duration
	intraMessageTimeout time.Duration
	interMessageTimeout time.Duration
	ringLock            sync.RWMutex
	ring                Ring
	msgHandlersLock     sync.RWMutex
	msgHandlers         map[uint64]MsgUnmarshaller
	connsLock           sync.RWMutex
	conns               map[string]*ringConn
	stateLock           sync.RWMutex
	state               int
	stateNowStoppedChan chan struct{}
}

func NewTCPMsgRing(r Ring) *TCPMsgRing {
	return &TCPMsgRing{
		ring:                r,
		msgHandlers:         make(map[uint64]MsgUnmarshaller),
		conns:               make(map[string]*ringConn),
		chunkSize:           16 * 1024,
		connectionTimeout:   60 * time.Second,
		intraMessageTimeout: 2 * time.Second,
		interMessageTimeout: 2 * time.Hour,
	}
}

func (m *TCPMsgRing) Ring() Ring {
	m.ringLock.RLock()
	r := m.ring
	m.ringLock.RUnlock()
	return r
}

func (m *TCPMsgRing) SetRing(ring Ring) {
	m.ringLock.Lock()
	m.ring = ring
	m.ringLock.Unlock()
}

func (m *TCPMsgRing) MaxMsgLength() uint64 {
	return math.MaxUint64
}

func (m *TCPMsgRing) SetMsgHandler(msgType uint64, handler MsgUnmarshaller) {
	m.msgHandlersLock.Lock()
	m.msgHandlers[uint64(msgType)] = handler
	m.msgHandlersLock.Unlock()
}

func (m *TCPMsgRing) connection(addr string) *ringConn {
	if m.Stopped() {
		return nil
	}
	m.connsLock.RLock()
	conn := m.conns[addr]
	m.connsLock.RUnlock()
	if conn != nil && conn != _WORKING {
		return conn
	}
	m.connsLock.Lock()
	conn = m.conns[addr]
	if conn != nil && conn != _WORKING {
		m.connsLock.Unlock()
		return conn
	}
	if m.Stopped() {
		m.connsLock.Unlock()
		return nil
	}
	m.conns[addr] = _WORKING
	m.connsLock.Unlock()
	go func() {
		tcpconn, err := net.DialTimeout("tcp", addr, m.connectionTimeout)
		if err != nil {
			// TODO: Log error.
			// TODO: Add a configurable sleep here to limit the quickness of
			// reconnect tries.
			time.Sleep(time.Second)
			m.connsLock.Lock()
			delete(m.conns, addr)
			m.connsLock.Unlock()
		} else {
			go m.handleTCPConnection(addr, tcpconn)
		}
	}()
	return nil
}

func (m *TCPMsgRing) disconnect(addr string, conn *ringConn) {
	m.connsLock.Lock()
	if m.conns[addr] != conn {
		m.connsLock.Unlock()
		return
	}
	m.conns[addr] = _WORKING
	m.connsLock.Unlock()
	go func() {
		if err := conn.conn.Close(); err != nil {
			log.Println("tcp msg ring disconnect close err:", err)
		}
		// TODO: Add a configurable sleep here to limit the quickness of
		// reconnect tries.
		time.Sleep(time.Second)
		m.connsLock.Lock()
		delete(m.conns, addr)
		m.connsLock.Unlock()
	}()
}

// handleTCPConnection will not return until the connection is closed; so be
// sure to run it in a background goroutine.
func (m *TCPMsgRing) handleTCPConnection(addr string, tcpconn net.Conn) {
	// TODO: trade version numbers and local ids
	conn := &ringConn{
		addr:   addr,
		conn:   tcpconn,
		reader: newTimeoutReader(tcpconn, m.chunkSize, m.intraMessageTimeout),
		writer: newTimeoutWriter(tcpconn, m.chunkSize, m.intraMessageTimeout),
	}
	m.connsLock.Lock()
	m.conns[addr] = conn
	m.connsLock.Unlock()
	m.handleConnection(conn)
	m.disconnect(addr, conn)
}

func (m *TCPMsgRing) handleConnection(conn *ringConn) {
	for !m.Stopped() {
		if err := m.handleOneMessage(conn); err != nil {
			// TODO: We need better log handling. Some places are just a todo
			// and some places shoot stuff out the default logger, like here.
			log.Println("handleOneMessage error:", err)
			break
		}
	}
}

func (m *TCPMsgRing) handleOneMessage(conn *ringConn) error {
	var msgType uint64
	conn.reader.Timeout = m.interMessageTimeout
	b, err := conn.reader.ReadByte()
	conn.reader.Timeout = m.intraMessageTimeout
	if err != nil {
		return err
	}
	msgType = uint64(b)
	for i := 1; i < 8; i++ {
		b, err = conn.reader.ReadByte()
		if err != nil {
			return err
		}
		msgType <<= 8
		msgType |= uint64(b)
	}
	m.msgHandlersLock.Lock()
	handler := m.msgHandlers[msgType]
	m.msgHandlersLock.Unlock()
	if handler == nil {
		// TODO: This should read and discard the unknown message instead of
		// causing a disconnection.
		return fmt.Errorf("no handler for MsgType %x", msgType)
	}
	var length uint64
	for i := 0; i < 8; i++ {
		b, err = conn.reader.ReadByte()
		if err != nil {
			return err
		}
		length <<= 8
		length |= uint64(b)
	}
	// NOTE: The conn.reader has a Timeout that would trigger on actual reads
	// the handler does, but if the handler goes off to do some long running
	// processing and does not attempt any reader operations for a while, the
	// timeout would have no effect. However, using time.After for every
	// message is overly expensive, so bad handler code is just an acceptable
	// risk here.
	consumed, err := handler(conn.reader, length)
	if consumed != length {
		if err == nil {
			err = fmt.Errorf("did not read %d bytes; only read %d", length, consumed)
		}
	}
	if err != nil {
		return err
	}
	return nil
}

func (m *TCPMsgRing) MsgToNode(nodeID uint64, msg Msg) {
	node := m.Ring().Node(nodeID)
	if node != nil {
		m.msgToNode(msg, node)
	}
	msg.Done()
}

func (m *TCPMsgRing) msgToNode(msg Msg, node Node) {
	addr := node.Address(m.addressIndex)
	conn := m.connection(addr)
	if conn == nil {
		// TODO: Log, or count as a metric, or something.
		return
	}
	// NOTE: If there are a lot of messages to be sent to a node, this lock
	// wait could get significant. However, using a time.After is too
	// expensive. Perhaps we can refactor to place messages on a buffered
	// channel where we can more easily detect a full channel and immediately
	// drop additional messages until the channel has space.
	conn.writerLock.Lock()
	disconn := func(err error) {
		// TODO: Whatever we end up doing for logging, metrics, etc. should be
		// done very quickly or in the background.
		log.Println("msgToNode error:", err)
		m.disconnect(addr, conn)
		conn.writerLock.Unlock()
	}
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, msg.MsgType())
	_, err := conn.writer.Write(b)
	if err != nil {
		disconn(err)
		return
	}
	binary.BigEndian.PutUint64(b, msg.MsgLength())
	_, err = conn.writer.Write(b)
	if err != nil {
		disconn(err)
		return
	}
	length, err := msg.WriteContent(conn.writer)
	if err != nil {
		disconn(err)
		return
	}
	err = conn.writer.Flush()
	if err != nil {
		disconn(err)
		return
	}
	if length != msg.MsgLength() {
		disconn(fmt.Errorf("incorrect message length sent: %d != %d", length, msg.MsgLength()))
		return
	}
	conn.writerLock.Unlock()
}

func (m *TCPMsgRing) MsgToOtherReplicas(ringVersion int64, partition uint32, msg Msg) {
	r := m.Ring()
	if ringVersion != r.Version() {
		msg.Done()
		return
	}
	nodes := r.ResponsibleNodes(partition)
	retchan := make(chan struct{}, len(nodes))
	msgToNodeConc := func(n Node) {
		m.msgToNode(msg, n)
		retchan <- struct{}{}
	}
	localNode := r.LocalNode()
	var localID uint64
	if localNode != nil {
		localID = localNode.ID()
	}
	sent := 0
	for _, node := range nodes {
		if node.ID() != localID {
			go msgToNodeConc(node)
			sent++
		}
	}
	for ; sent > 0; sent-- {
		<-retchan
	}
	msg.Done()
}

// Start will launch a goroutine that will listen on the configured TCP port,
// accepting new connections and processing messages from those connections.
// The "chan error" that is returned may be read from to obtain any error the
// goroutine encounters or nil if the goroutine exits with no error due to
// Stop() being called. Note that if Stop() is never called and an error is
// never encountered, reading from this returned "chan error" will never
// return.
func (m *TCPMsgRing) Start() chan error {
	returnChan := make(chan error, 1)
	m.stateLock.Lock()
	switch m.state {
	case _STOPPED:
		m.state = _RUNNING
		go m.listen(returnChan)
	case _RUNNING:
		returnChan <- fmt.Errorf("already running")
	case _STOPPING:
		returnChan <- fmt.Errorf("stopping in progress")
	default:
		returnChan <- fmt.Errorf("unexpected state value %d", m.state)
	}
	m.stateLock.Unlock()
	return returnChan
}

func (m *TCPMsgRing) listen(returnChan chan error) {
	node := m.Ring().LocalNode()
	tcpAddr, err := net.ResolveTCPAddr("tcp", node.Address(m.addressIndex))
	if err != nil {
		returnChan <- err
		return
	}
	server, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		returnChan <- err
		return
	}
	var tcpconn net.Conn
	for !m.Stopped() {
		server.SetDeadline(time.Now().Add(time.Second))
		tcpconn, err = server.AcceptTCP()
		if err != nil {
			if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
				err = nil
				continue
			}
			log.Println("Listen/AcceptTCP error:", err)
			server.Close()
			break
		}
		addr := tcpconn.RemoteAddr().String()
		m.connsLock.Lock()
		c := m.conns[addr]
		if c != nil {
			c.conn.Close()
		}
		m.conns[addr] = _WORKING
		m.connsLock.Unlock()
		go m.handleTCPConnection(addr, tcpconn)
	}
	m.stateLock.Lock()
	m.state = _STOPPED
	if m.stateNowStoppedChan != nil {
		m.stateNowStoppedChan <- struct{}{}
		m.stateNowStoppedChan = nil
	}
	m.stateLock.Unlock()
	returnChan <- err
}

// Stop will send a stop signal the goroutine launched by Start(). When this
// method returns, the TCPMsgRing will not be listening for new incoming
// connections. It is okay to call Stop() even if the TCPMsgRing is already
// Stopped().
func (m *TCPMsgRing) Stop() {
	var c chan struct{}
	m.stateLock.Lock()
	if m.state == _RUNNING {
		m.state = _STOPPING
		c = make(chan struct{}, 1)
		m.stateNowStoppedChan = c
	}
	m.stateLock.Unlock()
	if c != nil {
		<-c
	}
}

// Stopped will be true if the TCPMsgRing is not currently listening for new
// connections.
func (m *TCPMsgRing) Stopped() bool {
	m.stateLock.RLock()
	s := m.state
	m.stateLock.RUnlock()
	return s == _STOPPED || s == _STOPPING
}
<commit_msg>Fixed a place where it should've been a read lock<commit_after>package ring

import (
	"encoding/binary"
	"fmt"
	"log"
	"math"
	"net"
	"sync"
	"time"
)

type ringConn struct {
	addr       string
	conn       net.Conn
	reader     *timeoutReader
	writerLock sync.Mutex
	writer     *timeoutWriter
}

// This is used as a placeholder in TCPMsgring.conns to indicate a goroutine is
// currently working on a connection with that address.
var _WORKING = &ringConn{}

// These are constants for use with TCPMsgRing.state
const (
	_STOPPED = iota
	_RUNNING
	_STOPPING
)

type TCPMsgRing struct {
	// addressIndex is the index given to a Node's Address method to determine
	// the network address to connect to (see Node's Address method for more
	// information).
	addressIndex        int
	chunkSize           int
	connectionTimeout   time.Duration
	intraMessageTimeout time.Duration
	interMessageTimeout time.Duration
	ringLock            sync.RWMutex
	ring                Ring
	msgHandlersLock     sync.RWMutex
	msgHandlers         map[uint64]MsgUnmarshaller
	connsLock           sync.RWMutex
	conns               map[string]*ringConn
	stateLock           sync.RWMutex
	state               int
	stateNowStoppedChan chan struct{}
}

func NewTCPMsgRing(r Ring) *TCPMsgRing {
	return &TCPMsgRing{
		ring:                r,
		msgHandlers:         make(map[uint64]MsgUnmarshaller),
		conns:               make(map[string]*ringConn),
		chunkSize:           16 * 1024,
		connectionTimeout:   60 * time.Second,
		intraMessageTimeout: 2 * time.Second,
		interMessageTimeout: 2 * time.Hour,
	}
}

func (m *TCPMsgRing) Ring() Ring {
	m.ringLock.RLock()
	r := m.ring
	m.ringLock.RUnlock()
	return r
}

func (m *TCPMsgRing) SetRing(ring Ring) {
	m.ringLock.Lock()
	m.ring = ring
	m.ringLock.Unlock()
}

func (m *TCPMsgRing) MaxMsgLength() uint64 {
	return math.MaxUint64
}

func (m *TCPMsgRing) SetMsgHandler(msgType uint64, handler MsgUnmarshaller) {
	m.msgHandlersLock.Lock()
	m.msgHandlers[uint64(msgType)] = handler
	m.msgHandlersLock.Unlock()
}

func (m *TCPMsgRing) connection(addr string) *ringConn {
	if m.Stopped() {
		return nil
	}
	m.connsLock.RLock()
	conn := m.conns[addr]
	m.connsLock.RUnlock()
	if conn != nil && conn != _WORKING {
		return conn
	}
	m.connsLock.Lock()
	conn = m.conns[addr]
	if conn != nil && conn != _WORKING {
		m.connsLock.Unlock()
		return conn
	}
	if m.Stopped() {
		m.connsLock.Unlock()
		return nil
	}
	m.conns[addr] = _WORKING
	m.connsLock.Unlock()
	go func() {
		tcpconn, err := net.DialTimeout("tcp", addr, m.connectionTimeout)
		if err != nil {
			// TODO: Log error.
			// TODO: Add a configurable sleep here to limit the quickness of
			// reconnect tries.
			time.Sleep(time.Second)
			m.connsLock.Lock()
			delete(m.conns, addr)
			m.connsLock.Unlock()
		} else {
			go m.handleTCPConnection(addr, tcpconn)
		}
	}()
	return nil
}

func (m *TCPMsgRing) disconnect(addr string, conn *ringConn) {
	m.connsLock.Lock()
	if m.conns[addr] != conn {
		m.connsLock.Unlock()
		return
	}
	m.conns[addr] = _WORKING
	m.connsLock.Unlock()
	go func() {
		if err := conn.conn.Close(); err != nil {
			log.Println("tcp msg ring disconnect close err:", err)
		}
		// TODO: Add a configurable sleep here to limit the quickness of
		// reconnect tries.
		time.Sleep(time.Second)
		m.connsLock.Lock()
		delete(m.conns, addr)
		m.connsLock.Unlock()
	}()
}

// handleTCPConnection will not return until the connection is closed; so be
// sure to run it in a background goroutine.
func (m *TCPMsgRing) handleTCPConnection(addr string, tcpconn net.Conn) {
	// TODO: trade version numbers and local ids
	conn := &ringConn{
		addr:   addr,
		conn:   tcpconn,
		reader: newTimeoutReader(tcpconn, m.chunkSize, m.intraMessageTimeout),
		writer: newTimeoutWriter(tcpconn, m.chunkSize, m.intraMessageTimeout),
	}
	m.connsLock.Lock()
	m.conns[addr] = conn
	m.connsLock.Unlock()
	m.handleConnection(conn)
	m.disconnect(addr, conn)
}

func (m *TCPMsgRing) handleConnection(conn *ringConn) {
	for !m.Stopped() {
		if err := m.handleOneMessage(conn); err != nil {
			// TODO: We need better log handling. Some places are just a todo
			// and some places shoot stuff out the default logger, like here.
			log.Println("handleOneMessage error:", err)
			break
		}
	}
}

func (m *TCPMsgRing) handleOneMessage(conn *ringConn) error {
	var msgType uint64
	conn.reader.Timeout = m.interMessageTimeout
	b, err := conn.reader.ReadByte()
	conn.reader.Timeout = m.intraMessageTimeout
	if err != nil {
		return err
	}
	msgType = uint64(b)
	for i := 1; i < 8; i++ {
		b, err = conn.reader.ReadByte()
		if err != nil {
			return err
		}
		msgType <<= 8
		msgType |= uint64(b)
	}
	m.msgHandlersLock.RLock()
	handler := m.msgHandlers[msgType]
	m.msgHandlersLock.RUnlock()
	if handler == nil {
		// TODO: This should read and discard the unknown message instead of
		// causing a disconnection.
		return fmt.Errorf("no handler for MsgType %x", msgType)
	}
	var length uint64
	for i := 0; i < 8; i++ {
		b, err = conn.reader.ReadByte()
		if err != nil {
			return err
		}
		length <<= 8
		length |= uint64(b)
	}
	// NOTE: The conn.reader has a Timeout that would trigger on actual reads
	// the handler does, but if the handler goes off to do some long running
	// processing and does not attempt any reader operations for a while, the
	// timeout would have no effect. However, using time.After for every
	// message is overly expensive, so bad handler code is just an acceptable
	// risk here.
	consumed, err := handler(conn.reader, length)
	if consumed != length {
		if err == nil {
			err = fmt.Errorf("did not read %d bytes; only read %d", length, consumed)
		}
	}
	if err != nil {
		return err
	}
	return nil
}

func (m *TCPMsgRing) MsgToNode(nodeID uint64, msg Msg) {
	node := m.Ring().Node(nodeID)
	if node != nil {
		m.msgToNode(msg, node)
	}
	msg.Done()
}

func (m *TCPMsgRing) msgToNode(msg Msg, node Node) {
	addr := node.Address(m.addressIndex)
	conn := m.connection(addr)
	if conn == nil {
		// TODO: Log, or count as a metric, or something.
		return
	}
	// NOTE: If there are a lot of messages to be sent to a node, this lock
	// wait could get significant. However, using a time.After is too
	// expensive. Perhaps we can refactor to place messages on a buffered
	// channel where we can more easily detect a full channel and immediately
	// drop additional messages until the channel has space.
	conn.writerLock.Lock()
	disconn := func(err error) {
		// TODO: Whatever we end up doing for logging, metrics, etc. should be
		// done very quickly or in the background.
		log.Println("msgToNode error:", err)
		m.disconnect(addr, conn)
		conn.writerLock.Unlock()
	}
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, msg.MsgType())
	_, err := conn.writer.Write(b)
	if err != nil {
		disconn(err)
		return
	}
	binary.BigEndian.PutUint64(b, msg.MsgLength())
	_, err = conn.writer.Write(b)
	if err != nil {
		disconn(err)
		return
	}
	length, err := msg.WriteContent(conn.writer)
	if err != nil {
		disconn(err)
		return
	}
	err = conn.writer.Flush()
	if err != nil {
		disconn(err)
		return
	}
	if length != msg.MsgLength() {
		disconn(fmt.Errorf("incorrect message length sent: %d != %d", length, msg.MsgLength()))
		return
	}
	conn.writerLock.Unlock()
}

func (m *TCPMsgRing) MsgToOtherReplicas(ringVersion int64, partition uint32, msg Msg) {
	r := m.Ring()
	if ringVersion != r.Version() {
		msg.Done()
		return
	}
	nodes := r.ResponsibleNodes(partition)
	retchan := make(chan struct{}, len(nodes))
	msgToNodeConc := func(n Node) {
		m.msgToNode(msg, n)
		retchan <- struct{}{}
	}
	localNode := r.LocalNode()
	var localID uint64
	if localNode != nil {
		localID = localNode.ID()
	}
	sent := 0
	for _, node := range nodes {
		if node.ID() != localID {
			go msgToNodeConc(node)
			sent++
		}
	}
	for ; sent > 0; sent-- {
		<-retchan
	}
	msg.Done()
}

// Start will launch a goroutine that will listen on the configured TCP port,
// accepting new connections and processing messages from those connections.
// The "chan error" that is returned may be read from to obtain any error the
// goroutine encounters or nil if the goroutine exits with no error due to
// Stop() being called. Note that if Stop() is never called and an error is
// never encountered, reading from this returned "chan error" will never
// return.
func (m *TCPMsgRing) Start() chan error {
	returnChan := make(chan error, 1)
	m.stateLock.Lock()
	switch m.state {
	case _STOPPED:
		m.state = _RUNNING
		go m.listen(returnChan)
	case _RUNNING:
		returnChan <- fmt.Errorf("already running")
	case _STOPPING:
		returnChan <- fmt.Errorf("stopping in progress")
	default:
		returnChan <- fmt.Errorf("unexpected state value %d", m.state)
	}
	m.stateLock.Unlock()
	return returnChan
}

func (m *TCPMsgRing) listen(returnChan chan error) {
	node := m.Ring().LocalNode()
	tcpAddr, err := net.ResolveTCPAddr("tcp", node.Address(m.addressIndex))
	if err != nil {
		returnChan <- err
		return
	}
	server, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		returnChan <- err
		return
	}
	var tcpconn net.Conn
	for !m.Stopped() {
		server.SetDeadline(time.Now().Add(time.Second))
		tcpconn, err = server.AcceptTCP()
		if err != nil {
			if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
				err = nil
				continue
			}
			log.Println("Listen/AcceptTCP error:", err)
			server.Close()
			break
		}
		addr := tcpconn.RemoteAddr().String()
		m.connsLock.Lock()
		c := m.conns[addr]
		if c != nil {
			c.conn.Close()
		}
		m.conns[addr] = _WORKING
		m.connsLock.Unlock()
		go m.handleTCPConnection(addr, tcpconn)
	}
	m.stateLock.Lock()
	m.state = _STOPPED
	if m.stateNowStoppedChan != nil {
		m.stateNowStoppedChan <- struct{}{}
		m.stateNowStoppedChan = nil
	}
	m.stateLock.Unlock()
	returnChan <- err
}

// Stop will send a stop signal the goroutine launched by Start(). When this
// method returns, the TCPMsgRing will not be listening for new incoming
// connections. It is okay to call Stop() even if the TCPMsgRing is already
// Stopped().
func (m *TCPMsgRing) Stop() {
	var c chan struct{}
	m.stateLock.Lock()
	if m.state == _RUNNING {
		m.state = _STOPPING
		c = make(chan struct{}, 1)
		m.stateNowStoppedChan = c
	}
	m.stateLock.Unlock()
	if c != nil {
		<-c
	}
}

// Stopped will be true if the TCPMsgRing is not currently listening for new
// connections.
func (m *TCPMsgRing) Stopped() bool {
	m.stateLock.RLock()
	s := m.state
	m.stateLock.RUnlock()
	return s == _STOPPED || s == _STOPPING
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/appleboy/com/random"
	"github.com/appleboy/easyssh-proxy"
	"github.com/fatih/color"
)

var (
	errMissingHost           = errors.New("Error: missing server host")
	errMissingPasswordOrKey  = errors.New("Error: can't connect without a private SSH key or password")
	errSetPasswordandKey     = errors.New("can't set password and key at the same time")
	errMissingSourceOrTarget = errors.New("missing source or target config")
)

type (
	// Repo information.
	Repo struct {
		Owner string
		Name  string
	}

	// Build information.
	Build struct {
		Event   string
		Number  int
		Commit  string
		Message string
		Branch  string
		Author  string
		Status  string
		Link    string
	}

	// Config for the plugin.
	Config struct {
		Host            []string
		Port            string
		Username        string
		Password        string
		Key             string
		Passphrase      string
		Fingerprint     string
		KeyPath         string
		Timeout         time.Duration
		CommandTimeout  time.Duration
		Target          []string
		Source          []string
		Remove          bool
		StripComponents int
		TarExec         string
		TarTmpPath      string
		Proxy           easyssh.DefaultConfig
		Debug           bool
		Overwrite       bool
		Ciphers         []string
	}

	// Plugin values.
	Plugin struct {
		Repo     Repo
		Build    Build
		Config   Config
		DestFile string
	}

	copyError struct {
		host    string
		message string
	}
)

func (e copyError) Error() string {
	return fmt.Sprintf("error copy file to dest: %s, error message: %s\n", e.host, e.message)
}

func trimPath(keys []string) []string {
	var newKeys []string

	for _, value := range keys {
		value = strings.Trim(value, " ")
		if len(value) == 0 {
			continue
		}

		newKeys = append(newKeys, value)
	}

	return newKeys
}

func globList(paths []string) fileList {
	var list fileList

	for _, pattern := range paths {
		ignore := false
		pattern = strings.Trim(pattern, " ")
		if string(pattern[0]) == "!" {
			pattern = pattern[1:]
			ignore = true
		}
		matches, err := filepath.Glob(pattern)
		if err != nil {
			fmt.Printf("Glob error for %q: %s\n", pattern, err)
			continue
		}

		if ignore {
			list.Ignore = append(list.Ignore, matches...)
		} else {
			list.Source = append(list.Source, matches...)
		}
	}

	return list
}

func buildArgs(tar string, files fileList) []string {
	args := []string{}
	if len(files.Ignore) > 0 {
		for _, v := range files.Ignore {
			args = append(args, "--exclude")
			args = append(args, v)
		}
	}
	args = append(args, "-cf")
	args = append(args, getRealPath(tar))
	args = append(args, files.Source...)

	return args
}

func (p Plugin) log(host string, message ...interface{}) {
	if count := len(p.Config.Host); count == 1 {
		fmt.Printf("%s", fmt.Sprintln(message...))
	} else {
		fmt.Printf("%s: %s", host, fmt.Sprintln(message...))
	}
}

func (p *Plugin) removeDestFile(ssh *easyssh.MakeConfig) error {
	p.log(ssh.Server, "remove file", p.DestFile)
	_, errStr, _, err := ssh.Run(fmt.Sprintf("rm -rf %s", p.DestFile), p.Config.CommandTimeout)

	if err != nil {
		return err
	}

	if errStr != "" {
		return errors.New(errStr)
	}

	return nil
}

func (p *Plugin) removeAllDestFile() error {
	for _, host := range p.Config.Host {
		ssh := &easyssh.MakeConfig{
			Server:      host,
			User:        p.Config.Username,
			Password:    p.Config.Password,
			Port:        p.Config.Port,
			Key:         p.Config.Key,
			KeyPath:     p.Config.KeyPath,
			Passphrase:  p.Config.Passphrase,
			Timeout:     p.Config.Timeout,
			Fingerprint: p.Config.Fingerprint,
			Proxy: easyssh.DefaultConfig{
				Server:      p.Config.Proxy.Server,
				User:        p.Config.Proxy.User,
				Password:    p.Config.Proxy.Password,
				Port:        p.Config.Proxy.Port,
				Key:         p.Config.Proxy.Key,
				KeyPath:     p.Config.Proxy.KeyPath,
				Passphrase:  p.Config.Proxy.Passphrase,
				Timeout:     p.Config.Proxy.Timeout,
				Fingerprint: p.Config.Proxy.Fingerprint,
			},
		}

		// remove tar file
		err := p.removeDestFile(ssh)
		if err != nil {
			return err
		}
	}

	return nil
}

type fileList struct {
	Ignore []string
	Source []string
}

func (p *Plugin) buildArgs(target string) []string {
	args := []string{}

	args = append(args,
		p.Config.TarExec,
		"-xf",
		p.DestFile,
	)

	if p.Config.StripComponents > 0 {
		args = append(args, "--strip-components")
		args = append(args, strconv.Itoa(p.Config.StripComponents))
	}

	if p.Config.Overwrite {
		args = append(args, "--overwrite")
	}

	args = append(args,
		"-C",
		target,
	)

	return args
}

// Exec executes the plugin.
func (p *Plugin) Exec() error {
	if len(p.Config.Host) == 0 {
		return errMissingHost
	}

	if len(p.Config.Key) == 0 && len(p.Config.Password) == 0 && len(p.Config.KeyPath) == 0 {
		return errMissingPasswordOrKey
	}

	if len(p.Config.Key) != 0 && len(p.Config.Password) != 0 {
		return errSetPasswordandKey
	}

	if len(p.Config.Source) == 0 || len(p.Config.Target) == 0 {
		return errMissingSourceOrTarget
	}

	files := globList(trimPath(p.Config.Source))
	p.DestFile = fmt.Sprintf("%s.tar", random.String(10))

	// create a temporary file for the archive
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		return err
	}
	tar := filepath.Join(dir, p.DestFile)

	// run archive command
	fmt.Println("tar all files into " + tar)
	args := buildArgs(tar, files)
	cmd := exec.Command(p.Config.TarExec, args...)
	if p.Config.Debug {
		fmt.Println("$", strings.Join(cmd.Args, " "))
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	wg := sync.WaitGroup{}
	wg.Add(len(p.Config.Host))
	errChannel := make(chan error)
	finished := make(chan struct{})
	for _, host := range p.Config.Host {
		go func(host string) {
			// Create MakeConfig instance with remote username, server address and path to private key.
			ssh := &easyssh.MakeConfig{
				Server:      host,
				User:        p.Config.Username,
				Password:    p.Config.Password,
				Port:        p.Config.Port,
				Key:         p.Config.Key,
				KeyPath:     p.Config.KeyPath,
				Passphrase:  p.Config.Passphrase,
				Timeout:     p.Config.Timeout,
				Ciphers:     p.Config.Ciphers,
				Fingerprint: p.Config.Fingerprint,
				Proxy: easyssh.DefaultConfig{
					Server:      p.Config.Proxy.Server,
					User:        p.Config.Proxy.User,
					Password:    p.Config.Proxy.Password,
					Port:        p.Config.Proxy.Port,
					Key:         p.Config.Proxy.Key,
					KeyPath:     p.Config.Proxy.KeyPath,
					Passphrase:  p.Config.Proxy.Passphrase,
					Timeout:     p.Config.Proxy.Timeout,
					Ciphers:     p.Config.Proxy.Ciphers,
					Fingerprint: p.Config.Proxy.Fingerprint,
				},
			}

			// upload file to the tmp path
			p.DestFile = fmt.Sprintf("%s%s", p.Config.TarTmpPath, p.DestFile)

			// Call Scp method with file you want to upload to remote server.
			p.log(host, "scp file to server.")
			err := ssh.Scp(tar, p.DestFile)

			if err != nil {
				errChannel <- copyError{host, err.Error()}
				return
			}

			for _, target := range p.Config.Target {
				// remove target before upload data
				if p.Config.Remove {
					p.log(host, "Remove target folder:", target)

					_, _, _, err := ssh.Run(fmt.Sprintf("rm -rf %s", target), p.Config.CommandTimeout)

					if err != nil {
						errChannel <- err
						return
					}
				}

				// mkdir path
				p.log(host, "create folder", target)
				_, errStr, _, err := ssh.Run(fmt.Sprintf("mkdir -p %s", target), p.Config.CommandTimeout)
				if err != nil {
					errChannel <- err
					return
				}

				if len(errStr) != 0 {
					errChannel <- fmt.Errorf(errStr)
					return
				}

				// untar file
				p.log(host, "untar file", p.DestFile)
				commamd := strings.Join(p.buildArgs(target), " ")
				if p.Config.Debug {
					fmt.Println("$", commamd)
				}
				outStr, errStr, _, err := ssh.Run(commamd, p.Config.CommandTimeout)

				if outStr != "" {
					p.log(host, "output: ", outStr)
				}

				if errStr != "" {
					p.log(host, "error: ", errStr)
				}

				if err != nil {
					errChannel <- err
					return
				}
			}

			// remove tar file
			err = p.removeDestFile(ssh)
			if err != nil {
				errChannel <- err
				return
			}

			wg.Done()

		}(host)
	}

	go func() {
		wg.Wait()
		close(finished)
	}()

	select {
	case <-finished:
	case err := <-errChannel:
		if err != nil {
			c := color.New(color.FgRed)
			c.Println("drone-scp error: ", err)
			if _, ok := err.(copyError); !ok {
				fmt.Println("drone-scp rollback: remove all target tmp file")
				p.removeAllDestFile()
			}
			return err
		}
	}

	fmt.Println("===================================================")
	fmt.Println("✅ Successfully executed transfer data to all host")
	fmt.Println("===================================================")

	return nil
}
<commit_msg>chore: missing Ciphers in removeAllDestFile<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/appleboy/com/random"
	"github.com/appleboy/easyssh-proxy"
	"github.com/fatih/color"
)

var (
	errMissingHost           = errors.New("Error: missing server host")
	errMissingPasswordOrKey  = errors.New("Error: can't connect without a private SSH key or password")
	errSetPasswordandKey     = errors.New("can't set password and key at the same time")
	errMissingSourceOrTarget = errors.New("missing source or target config")
)

type (
	// Repo information.
	Repo struct {
		Owner string
		Name  string
	}

	// Build information.
	Build struct {
		Event   string
		Number  int
		Commit  string
		Message string
		Branch  string
		Author  string
		Status  string
		Link    string
	}

	// Config for the plugin.
	Config struct {
		Host            []string
		Port            string
		Username        string
		Password        string
		Key             string
		Passphrase      string
		Fingerprint     string
		KeyPath         string
		Timeout         time.Duration
		CommandTimeout  time.Duration
		Target          []string
		Source          []string
		Remove          bool
		StripComponents int
		TarExec         string
		TarTmpPath      string
		Proxy           easyssh.DefaultConfig
		Debug           bool
		Overwrite       bool
		Ciphers         []string
	}

	// Plugin values.
	Plugin struct {
		Repo     Repo
		Build    Build
		Config   Config
		DestFile string
	}

	copyError struct {
		host    string
		message string
	}
)

func (e copyError) Error() string {
	return fmt.Sprintf("error copy file to dest: %s, error message: %s\n", e.host, e.message)
}

func trimPath(keys []string) []string {
	var newKeys []string

	for _, value := range keys {
		value = strings.Trim(value, " ")
		if len(value) == 0 {
			continue
		}

		newKeys = append(newKeys, value)
	}

	return newKeys
}

func globList(paths []string) fileList {
	var list fileList

	for _, pattern := range paths {
		ignore := false
		pattern = strings.Trim(pattern, " ")
		if string(pattern[0]) == "!" {
			pattern = pattern[1:]
			ignore = true
		}
		matches, err := filepath.Glob(pattern)
		if err != nil {
			fmt.Printf("Glob error for %q: %s\n", pattern, err)
			continue
		}

		if ignore {
			list.Ignore = append(list.Ignore, matches...)
		} else {
			list.Source = append(list.Source, matches...)
		}
	}

	return list
}

func buildArgs(tar string, files fileList) []string {
	args := []string{}
	if len(files.Ignore) > 0 {
		for _, v := range files.Ignore {
			args = append(args, "--exclude")
			args = append(args, v)
		}
	}
	args = append(args, "-cf")
	args = append(args, getRealPath(tar))
	args = append(args, files.Source...)

	return args
}

func (p Plugin) log(host string, message ...interface{}) {
	if count := len(p.Config.Host); count == 1 {
		fmt.Printf("%s", fmt.Sprintln(message...))
	} else {
		fmt.Printf("%s: %s", host, fmt.Sprintln(message...))
	}
}

func (p *Plugin) removeDestFile(ssh *easyssh.MakeConfig) error {
	p.log(ssh.Server, "remove file", p.DestFile)
	_, errStr, _, err := ssh.Run(fmt.Sprintf("rm -rf %s", p.DestFile), p.Config.CommandTimeout)

	if err != nil {
		return err
	}

	if errStr != "" {
		return errors.New(errStr)
	}

	return nil
}

func (p *Plugin) removeAllDestFile() error {
	for _, host := range p.Config.Host {
		ssh := &easyssh.MakeConfig{
			Server:      host,
			User:        p.Config.Username,
			Password:    p.Config.Password,
			Port:        p.Config.Port,
			Key:         p.Config.Key,
			KeyPath:     p.Config.KeyPath,
			Passphrase:  p.Config.Passphrase,
			Timeout:     p.Config.Timeout,
			Ciphers:     p.Config.Ciphers,
			Fingerprint: p.Config.Fingerprint,
			Proxy: easyssh.DefaultConfig{
				Server:      p.Config.Proxy.Server,
				User:        p.Config.Proxy.User,
				Password:    p.Config.Proxy.Password,
				Port:        p.Config.Proxy.Port,
				Key:         p.Config.Proxy.Key,
				KeyPath:     p.Config.Proxy.KeyPath,
				Passphrase:  p.Config.Proxy.Passphrase,
				Timeout:     p.Config.Proxy.Timeout,
				Ciphers:     p.Config.Proxy.Ciphers,
				Fingerprint: p.Config.Proxy.Fingerprint,
			},
		}

		// remove tar file
		err := p.removeDestFile(ssh)
		if err != nil {
			return err
		}
	}

	return nil
}

type fileList struct {
	Ignore []string
	Source []string
}

func (p *Plugin) buildArgs(target string) []string {
	args := []string{}

	args = append(args,
		p.Config.TarExec,
		"-xf",
		p.DestFile,
	)

	if p.Config.StripComponents > 0 {
		args = append(args, "--strip-components")
		args = append(args, strconv.Itoa(p.Config.StripComponents))
	}

	if p.Config.Overwrite {
		args = append(args, "--overwrite")
	}

	args = append(args,
		"-C",
		target,
	)

	return args
}

// Exec executes the plugin.
func (p *Plugin) Exec() error {
	if len(p.Config.Host) == 0 {
		return errMissingHost
	}

	if len(p.Config.Key) == 0 && len(p.Config.Password) == 0 && len(p.Config.KeyPath) == 0 {
		return errMissingPasswordOrKey
	}

	if len(p.Config.Key) != 0 && len(p.Config.Password) != 0 {
		return errSetPasswordandKey
	}

	if len(p.Config.Source) == 0 || len(p.Config.Target) == 0 {
		return errMissingSourceOrTarget
	}

	files := globList(trimPath(p.Config.Source))
	p.DestFile = fmt.Sprintf("%s.tar", random.String(10))

	// create a temporary file for the archive
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		return err
	}
	tar := filepath.Join(dir, p.DestFile)

	// run archive command
	fmt.Println("tar all files into " + tar)
	args := buildArgs(tar, files)
	cmd := exec.Command(p.Config.TarExec, args...)
	if p.Config.Debug {
		fmt.Println("$", strings.Join(cmd.Args, " "))
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	wg := sync.WaitGroup{}
	wg.Add(len(p.Config.Host))
	errChannel := make(chan error)
	finished := make(chan struct{})
	for _, host := range p.Config.Host {
		go func(host string) {
			// Create MakeConfig instance with remote username, server address and path to private key.
			ssh := &easyssh.MakeConfig{
				Server:      host,
				User:        p.Config.Username,
				Password:    p.Config.Password,
				Port:        p.Config.Port,
				Key:         p.Config.Key,
				KeyPath:     p.Config.KeyPath,
				Passphrase:  p.Config.Passphrase,
				Timeout:     p.Config.Timeout,
				Ciphers:     p.Config.Ciphers,
				Fingerprint: p.Config.Fingerprint,
				Proxy: easyssh.DefaultConfig{
					Server:      p.Config.Proxy.Server,
					User:        p.Config.Proxy.User,
					Password:    p.Config.Proxy.Password,
					Port:        p.Config.Proxy.Port,
					Key:         p.Config.Proxy.Key,
					KeyPath:     p.Config.Proxy.KeyPath,
					Passphrase:  p.Config.Proxy.Passphrase,
					Timeout:     p.Config.Proxy.Timeout,
					Ciphers:     p.Config.Proxy.Ciphers,
					Fingerprint: p.Config.Proxy.Fingerprint,
				},
			}

			// upload file to the tmp path
			p.DestFile = fmt.Sprintf("%s%s", p.Config.TarTmpPath, p.DestFile)

			// Call Scp method with file you want to upload to remote server.
			p.log(host, "scp file to server.")
			err := ssh.Scp(tar, p.DestFile)

			if err != nil {
				errChannel <- copyError{host, err.Error()}
				return
			}

			for _, target := range p.Config.Target {
				// remove target before upload data
				if p.Config.Remove {
					p.log(host, "Remove target folder:", target)

					_, _, _, err := ssh.Run(fmt.Sprintf("rm -rf %s", target), p.Config.CommandTimeout)

					if err != nil {
						errChannel <- err
						return
					}
				}

				// mkdir path
				p.log(host, "create folder", target)
				_, errStr, _, err := ssh.Run(fmt.Sprintf("mkdir -p %s", target), p.Config.CommandTimeout)
				if err != nil {
					errChannel <- err
					return
				}

				if len(errStr) != 0 {
					errChannel <- fmt.Errorf(errStr)
					return
				}

				// untar file
				p.log(host, "untar file", p.DestFile)
				commamd := strings.Join(p.buildArgs(target), " ")
				if p.Config.Debug {
					fmt.Println("$", commamd)
				}
				outStr, errStr, _, err := ssh.Run(commamd, p.Config.CommandTimeout)

				if outStr != "" {
					p.log(host, "output: ", outStr)
				}

				if errStr != "" {
					p.log(host, "error: ", errStr)
				}

				if err != nil {
					errChannel <- err
					return
				}
			}

			// remove tar file
			err = p.removeDestFile(ssh)
			if err != nil {
				errChannel <- err
				return
			}

			wg.Done()

		}(host)
	}

	go func() {
		wg.Wait()
		close(finished)
	}()

	select {
	case <-finished:
	case err := <-errChannel:
		if err != nil {
			c := color.New(color.FgRed)
			c.Println("drone-scp error: ", err)
			if _, ok := err.(copyError); !ok {
				fmt.Println("drone-scp rollback: remove all target tmp file")
				p.removeAllDestFile()
			}
			return err
		}
	}

	fmt.Println("===================================================")
	fmt.Println("✅ Successfully executed transfer data to all host")
	fmt.Println("===================================================")

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"bytes"
	"flag"
	"log"
	"time"
	"os"
	"os/signal"
	"os/exec"
	"net/url"
	"strings"
	"fmt"

	"github.com/gorilla/websocket"
	"github.com/shirou/gopsutil/mem"
	// "github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/host"
)

type ServerStat struct {
	// Host
	HostName             string  `json:"hostname"`
	HostID               string  `json:"hostid"`
	VirtualizationSystem string  `json:"virtualizationSystem"`
  // mem.VirtualMemoryStat
	Total                uint64  `json:"total"`
	Available            uint64  `json:"available"`
	UsedPercent          float64 `json:"usedPercent"`
	// DiskIO map[string]disk.IOCountersStat
	DiskIO               string  `json:"diskIO"`
	// IoTime            uint64  `json:"ioTime"`
	// WeightedIO        uint64  `json:"weightedIO"`
	// Time
	Time                 string  `json:"time"`

	// Cpu
	// Cpu    []cpu.TimesStat         `json:"-"`

	ApacheStat float64 `json:"apacheStat"`
}

var addr = flag.String("addr", "localhost:8080", "monitoring address")

func main() {
	var buf bytes.Buffer
	flag.Parse()
	log.SetFlags(0)

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
	log.Printf("connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer c.Close()
		defer close(done)
		for {
			_, _, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
		}
	}()

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			d := GetServerStat()
			j, _ :=json.Marshal(d)
			buf.Write(j)
			err := c.WriteMessage(websocket.TextMessage, j)
			if err != nil {
				log.Println("write:", err)
				return
			}
		case <-interrupt:
			log.Println("interrupt")
			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			c.Close()
			return
		}
	}
}

func GetServerStat() (ServerStat) {
	var d ServerStat

	d = d.GetHostStat()
  d = d.GetMemoryStat()
	d = d.GetDiskIOStat()
	d = d.GetTime()
	d = d.GetApacheStat()
	return d
}

func (s ServerStat) GetHostStat() (ServerStat) {
	h, _ := host.Info()
	s.HostName             = h.Hostname
	s.HostID               = h.HostID
	s.VirtualizationSystem = h.VirtualizationSystem
	return s
}

func (s ServerStat) GetMemoryStat() (ServerStat) {
	m, _ := mem.VirtualMemory()
	s.Total = m.Total
	s.Available = m.Available
	s.UsedPercent = m.UsedPercent
	return s
}

func (s ServerStat) GetDiskIOStat() (ServerStat) {
	i, _ := disk.IOCounters()
	s.DiskIO = ConvertMapToString(i)
	return s
}

func ConvertMapToString(m map[string]disk.IOCountersStat) (string) {
	var str string

	str = "{"
	for k, v := range m {
		str  = str + string(k) + ":{"
		str = str + "ioTime:" + fmt.Sprint(v.IoTime) + ","
		str = str + "weightedIO:" + fmt.Sprint(v.WeightedIO) + "},"
	}
  str = strings.TrimRight(str, ",")
	str = str + "}"
	return str
}

func (s ServerStat)GetTime() (ServerStat) {
	now := time.Now()
	s.Time =fmt.Sprint(now)
	return s
}

func (s ServerStat)GetApacheStat() (ServerStat) {
	var dataLine int
	out, _ := exec.Command("apachectl", "status").Output()
	d :=string(out)

	lines := strings.Split(strings.TrimRight(d, "\n"), "\n")

	for k, v := range lines {
		if v == "Scoreboard Key:" {
			dataLine = k
			break
		}

	}

	board := lines[dataLine-4]
	board = board + lines[dataLine-3]
	board = board + lines[dataLine-2]
	all := len(strings.Split(board, ""))
	idles := strings.Count(board, "_") + strings.Count(board, ".")

	r := float64((all - idles)) / float64(all)

	s.ApacheStat = r
	return s
}
<commit_msg>use pointer<commit_after>package main

import (
	"encoding/json"
	"bytes"
	"flag"
	"log"
	"time"
	"os"
	"os/signal"
	"os/exec"
	"net/url"
	"strings"
	"fmt"

	"github.com/gorilla/websocket"
	"github.com/shirou/gopsutil/mem"
	// "github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/host"
)

type ServerStat struct {
	// Host
	HostName             string  `json:"hostname"`
	HostID               string  `json:"hostid"`
	VirtualizationSystem string  `json:"virtualizationSystem"`
  // mem.VirtualMemoryStat
	Total                uint64  `json:"total"`
	Available            uint64  `json:"available"`
	UsedPercent          float64 `json:"usedPercent"`
	// DiskIO map[string]disk.IOCountersStat
	DiskIO               string  `json:"diskIO"`
	// IoTime            uint64  `json:"ioTime"`
	// WeightedIO        uint64  `json:"weightedIO"`
	// Time
	Time                 string  `json:"time"`

	// Cpu
	// Cpu    []cpu.TimesStat         `json:"-"`

	ApacheStat float64 `json:"apacheStat"`
}

var addr = flag.String("addr", "localhost:8080", "monitoring address")

func main() {
	var buf bytes.Buffer
	flag.Parse()
	log.SetFlags(0)

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
	log.Printf("connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer c.Close()
		defer close(done)
		for {
			_, _, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
		}
	}()

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			d := GetServerStat()
			j, _ :=json.Marshal(d)
			buf.Write(j)
			err := c.WriteMessage(websocket.TextMessage, j)
			if err != nil {
				log.Println("write:", err)
				return
			}
		case <-interrupt:
			log.Println("interrupt")
			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			c.Close()
			return
		}
	}
}

func GetServerStat() (ServerStat) {
	var d ServerStat

	d.GetHostStat()
  d.GetMemoryStat()
	d.GetDiskIOStat()
	d.GetTime()
	d.GetApacheStat()
	return d
}

func (s *ServerStat) GetHostStat() {
	h, _ := host.Info()
	s.HostName             = h.Hostname
	s.HostID               = h.HostID
	s.VirtualizationSystem = h.VirtualizationSystem
}

func (s *ServerStat) GetMemoryStat() {
	m, _ := mem.VirtualMemory()
	s.Total = m.Total
	s.Available = m.Available
	s.UsedPercent = m.UsedPercent
}

func (s *ServerStat) GetDiskIOStat() {
	i, _ := disk.IOCounters()
	s.DiskIO = ConvertMapToString(i)
}

func ConvertMapToString(m map[string]disk.IOCountersStat) (string) {
	var str string

	str = "{"
	for k, v := range m {
		str  = str + string(k) + ":{"
		str = str + "ioTime:" + fmt.Sprint(v.IoTime) + ","
		str = str + "weightedIO:" + fmt.Sprint(v.WeightedIO) + "},"
	}
  str = strings.TrimRight(str, ",")
	str = str + "}"
	return str
}

func (s *ServerStat)GetTime() {
	now := time.Now()
	s.Time =fmt.Sprint(now)
}

func (s *ServerStat)GetApacheStat() {
	var dataLine int
	out, _ := exec.Command("apachectl", "status").Output()
	d :=string(out)

	lines := strings.Split(strings.TrimRight(d, "\n"), "\n")

	for k, v := range lines {
		if v == "Scoreboard Key:" {
			dataLine = k
			break
		}
	}

	board := lines[dataLine-4]
	board = board + lines[dataLine-3]
	board = board + lines[dataLine-2]
	all := len(strings.Split(board, ""))
	idles := strings.Count(board, "_") + strings.Count(board, ".")

	r := float64((all - idles)) / float64(all)

	s.ApacheStat = r
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"strings"

	"github.com/Zac-Garby/pluto/token"
	"github.com/fatih/color"
)

// Error represents a parsing error
type Error struct {
	Message    string
	Start, End token.Position
}

func (p *Parser) Err(msg string, start, end token.Position) {
	err := Error{
		Message: msg,
		Start:   start,
		End:     end,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) defaultErr(msg string) {
	err := Error{
		Message: msg,
		Start:   p.cur.Start,
		End:     p.cur.End,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) peekErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.peek.Type)

		p.Err(msg, p.peek.Start, p.peek.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.peek.Type)
		p.Err(msg, p.peek.Start, p.peek.End)
	}
}

func (p *Parser) curErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.cur.Type)

		p.Err(msg, p.cur.Start, p.cur.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.cur.Type)
		p.Err(msg, p.cur.Start, p.cur.End)
	}
}

func (p *Parser) unexpectedTokenErr(t token.Type) {
	msg := fmt.Sprintf("unexpected token: %s", t)
	p.defaultErr(msg)
}

func (p *Parser) printError(index int) {
	err := p.Errors[index]

	fmt.Printf("%s → %s\t%s\n", err.Start.String(), err.End.String(), err.Message)
}

func (p *Parser) printErrorVerbose(index int) {
	err := p.Errors[index]
	lines := strings.Split(p.text, "\n")

	red := color.New(color.FgRed).Add(color.Bold)
	grey := color.New(color.FgHiWhite)

	grey.Printf("    %d| ", err.Start.Line)
	fmt.Printf("%s\n", lines[err.Start.Line-1])
	red.Printf(
		"    %s %s%s\n",
		strings.Repeat(" ", len(fmt.Sprintf("%d", err.Start.Line))),
		strings.Repeat(" ", err.Start.Column),
		strings.Repeat("^", err.End.Column-err.Start.Column+1),
	)

	red.Printf("%s → %s", err.Start.String(), err.End.String())
	fmt.Printf("\t%s\n\n", err.Message)
}

// PrintErrors prints all parser errors in a nice format
func (p *Parser) PrintErrors() {
	for i := range p.Errors {
		if i == 0 {
			p.printErrorVerbose(i)
		} else {
			p.printError(i)
		}
	}
}
<commit_msg>Add a missing comment<commit_after>package parser

import (
	"fmt"
	"strings"

	"github.com/Zac-Garby/pluto/token"
	"github.com/fatih/color"
)

// Error represents a parsing error
type Error struct {
	Message    string
	Start, End token.Position
}

// Err creates an Error instance with the given arguments
func (p *Parser) Err(msg string, start, end token.Position) {
	err := Error{
		Message: msg,
		Start:   start,
		End:     end,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) defaultErr(msg string) {
	err := Error{
		Message: msg,
		Start:   p.cur.Start,
		End:     p.cur.End,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) peekErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.peek.Type)

		p.Err(msg, p.peek.Start, p.peek.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.peek.Type)
		p.Err(msg, p.peek.Start, p.peek.End)
	}
}

func (p *Parser) curErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.cur.Type)

		p.Err(msg, p.cur.Start, p.cur.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.cur.Type)
		p.Err(msg, p.cur.Start, p.cur.End)
	}
}

func (p *Parser) unexpectedTokenErr(t token.Type) {
	msg := fmt.Sprintf("unexpected token: %s", t)
	p.defaultErr(msg)
}

func (p *Parser) printError(index int) {
	err := p.Errors[index]

	fmt.Printf("%s → %s\t%s\n", err.Start.String(), err.End.String(), err.Message)
}

func (p *Parser) printErrorVerbose(index int) {
	err := p.Errors[index]
	lines := strings.Split(p.text, "\n")

	red := color.New(color.FgRed).Add(color.Bold)
	grey := color.New(color.FgHiWhite)

	grey.Printf("    %d| ", err.Start.Line)
	fmt.Printf("%s\n", lines[err.Start.Line-1])
	red.Printf(
		"    %s %s%s\n",
		strings.Repeat(" ", len(fmt.Sprintf("%d", err.Start.Line))),
		strings.Repeat(" ", err.Start.Column),
		strings.Repeat("^", err.End.Column-err.Start.Column+1),
	)

	red.Printf("%s → %s", err.Start.String(), err.End.String())
	fmt.Printf("\t%s\n\n", err.Message)
}

// PrintErrors prints all parser errors in a nice format
func (p *Parser) PrintErrors() {
	for i := range p.Errors {
		if i == 0 {
			p.printErrorVerbose(i)
		} else {
			p.printError(i)
		}
	}
}
<|endoftext|>


<commit_before>package parser

import (
  "fmt"
  "regexp"
  "bitbucket.org/yyuu/bs/strscan"
)

const (
  SPACES = iota
  BLOCK_COMMENT
  LINE_COMMENT
  VOID
  CHAR
  SHORT
  INT
  LONG
  STRUCT
  UNION
  ENUM
  STATIC
  EXTERN
  CONST
  SIGNED
  UNSIGNED
  IF
  ELSE
  SWITCH
  CASE
  DEFAULT
  WHILE
  DO
  FOR
  RETURN
  BREAK
  CONTINUE
  GOTO
  TYPEDEF
  IMPORT
  SIZEOF
  IDENTIFIER
  INTEGER
  CHARACTER
  STRING
  OPERATOR
)

var id2name map[int]string = map[int]string {
  SPACES: "SPACES",
  BLOCK_COMMENT: "BLOCK_COMMENT",
  LINE_COMMENT: "LINE_COMMENT",
  VOID: "VOID",
  CHAR: "CHAR",
  SHORT: "",
  INT: "INT",
  LONG: "LONG",
  STRUCT: "STRUCT",
  UNION: "UNION",
  ENUM: "ENUM",
  STATIC: "STATIC",
  EXTERN: "EXTERN",
  CONST: "CONST",
  SIGNED: "SIGNED",
  UNSIGNED: "UNSIGNED",
  IF: "IF",
  ELSE: "ELSE",
  SWITCH: "SWITCH",
  CASE: "CASE",
  DEFAULT: "DEFAULT",
  WHILE: "WHILE",
  DO: "DO",
  FOR: "FOR",
  RETURN: "RETURN",
  BREAK: "BREAK",
  CONTINUE: "CONTINUE",
  GOTO: "GOTO",
  TYPEDEF: "TYPEDEF",
  IMPORT: "IMPORT",
  SIZEOF: "SIZEOF",
  IDENTIFIER: "IDENTIFIER",
  INTEGER: "INTEGER",
  CHARACTER: "CHARACTER",
  STRING: "STRING",
  OPERATOR: "OPERATOR",
}

type Lexer struct {
  filename string
  scanner strscan.StringScanner
}

type Token struct {
  Id int
  Literal string
}

func (t *Token) ToString() string {
  name, ok := id2name[t.Id]
  if ok {
    return fmt.Sprintf("<Token:%s %q>", name, t.Literal)
  } else {
    return fmt.Sprintf("<Token:%d %q>", t.Id, t.Literal)
  }
}

func NewLexer(filename string, source string) *Lexer {
  return &Lexer {
    filename: filename,
    scanner: strscan.NewStringScanner(source),
  }
}

var keywords map[string]int = map[string]int {
  "void": VOID,
  "char": CHAR,
  "short": SHORT,
  "int": INT,
  "long": LONG,
  "struct": STRUCT,
  "union": UNION,
  "enum": ENUM,
  "static": STATIC,
  "extern": EXTERN,
  "const": CONST,
  "signed": SIGNED,
  "unsigned": UNSIGNED,
  "if": IF,
  "else": ELSE,
  "switch": SWITCH,
  "case": CASE,
  "default": DEFAULT,
  "while": WHILE,
  "do": DO,
  "for": FOR,
  "return": RETURN,
  "break": BREAK,
  "continue": CONTINUE,
  "goto": GOTO,
  "typedef": TYPEDEF,
  "import": IMPORT,
  "sizeof": SIZEOF,
}

func (self *Lexer) GetToken() (t *Token) {
  if self.scanner.IsEOS() {
    return nil
  }

  t = self.readSpaces()
  if t != nil {
    return t
  }

  t = self.readBlockComment()
  if t != nil {
    return t
  }
  t = self.readLineComment()
  if t != nil {
    return t
  }

  t = self.readKeyword()
  if t != nil {
    return t
  }

  t = self.readIdentifier()
  if t != nil {
    return t
  }

  t = self.readInteger()
  if t != nil {
    return t
  }

  t = self.readCharacter()
  if t != nil {
    return t
  }

  t = self.readString()
  if t != nil {
    return t
  }

  t = self.readOperator()
  if t != nil {
    return t
  }

  panic("lexer error")
}

func (self *Lexer) readBlockComment() *Token {
  s := self.scanner.Scan("/\\*")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("\\*/")
  if more == "" {
    panic("lexer error")
  }
  return &Token { BLOCK_COMMENT, s + more }
}

func (self *Lexer) readLineComment() *Token {
  s := self.scanner.Scan("//")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("(\n|\r\n|\r)")
  if more == "" {
    panic("lexer error")
  }
  return &Token { LINE_COMMENT, s + more }
}

func (self *Lexer) readSpaces() *Token {
  s := self.scanner.Scan("[ \t\n\r\f]+")
  if s == "" {
    return nil
  }
  return &Token { SPACES, s }
}

func (self *Lexer) readIdentifier() *Token {
  s := self.scanner.Scan("[_A-Za-z][_0-9A-Za-z]*")
  if s == "" {
    return nil
  }
  return &Token { IDENTIFIER, s }
}

func (self *Lexer) readInteger() *Token {
  s := self.scanner.Scan("([1-9][0-9]*U?L?|0[Xx][0-9A-Fa-f]+U?L?|0[0-7]*U?L?)")
  if s == "" {
    return nil
  }
  return &Token { INTEGER, s }
}

func (self *Lexer) readKeyword() *Token {
  for keyword, id := range keywords {
    s := self.scanner.Scan(regexp.QuoteMeta(keyword))
    if s != "" {
      return &Token { id, s }
    }
  }
  return nil
}

func (self *Lexer) readCharacter() *Token {
  s := self.scanner.Scan("'")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("'")
  if more == "" {
    panic("lexer error")
  }
  return &Token { CHARACTER, s + more }
}

func (self *Lexer) readString() *Token {
  s := self.scanner.Scan("\"")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("\"")
  if more == "" {
    panic("lexer error")
  }
  return &Token { STRING, s + more }
}

func (self *Lexer) readOperator() *Token {
  operators := []string {
    "<<=", ">>=", "...",
    "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=",
    "||", "&&", ">=", "<=", "==", "!=", "++", "--", ">>", "<<", "->",
    "=", ">", "<", ":", ";", "?", "{", "}", "(", ")", "+", "-", "!", "~",
    "*", "&", "|", "^", "&", "+", "-", "*", "/", "%", "[", "]", ".", ",",
  }
  for i := range operators {
    s := self.scanner.Scan(regexp.QuoteMeta(operators[i]))
    if s != "" {
      return &Token { OPERATOR, s }
    }
  }
  return nil
}
<commit_msg>Count `lineNumber` and `lineOffset` in lexer<commit_after>package parser

import (
  "fmt"
  "regexp"
  "strings"
  "bitbucket.org/yyuu/bs/strscan"
)

const (
  SPACES = iota
  BLOCK_COMMENT
  LINE_COMMENT
  VOID
  CHAR
  SHORT
  INT
  LONG
  STRUCT
  UNION
  ENUM
  STATIC
  EXTERN
  CONST
  SIGNED
  UNSIGNED
  IF
  ELSE
  SWITCH
  CASE
  DEFAULT
  WHILE
  DO
  FOR
  RETURN
  BREAK
  CONTINUE
  GOTO
  TYPEDEF
  IMPORT
  SIZEOF
  IDENTIFIER
  INTEGER
  CHARACTER
  STRING
  OPERATOR
)

var id2name map[int]string = map[int]string {
  SPACES: "SPACES",
  BLOCK_COMMENT: "BLOCK_COMMENT",
  LINE_COMMENT: "LINE_COMMENT",
  VOID: "VOID",
  CHAR: "CHAR",
  SHORT: "",
  INT: "INT",
  LONG: "LONG",
  STRUCT: "STRUCT",
  UNION: "UNION",
  ENUM: "ENUM",
  STATIC: "STATIC",
  EXTERN: "EXTERN",
  CONST: "CONST",
  SIGNED: "SIGNED",
  UNSIGNED: "UNSIGNED",
  IF: "IF",
  ELSE: "ELSE",
  SWITCH: "SWITCH",
  CASE: "CASE",
  DEFAULT: "DEFAULT",
  WHILE: "WHILE",
  DO: "DO",
  FOR: "FOR",
  RETURN: "RETURN",
  BREAK: "BREAK",
  CONTINUE: "CONTINUE",
  GOTO: "GOTO",
  TYPEDEF: "TYPEDEF",
  IMPORT: "IMPORT",
  SIZEOF: "SIZEOF",
  IDENTIFIER: "IDENTIFIER",
  INTEGER: "INTEGER",
  CHARACTER: "CHARACTER",
  STRING: "STRING",
  OPERATOR: "OPERATOR",
}

type Lexer struct {
  scanner strscan.StringScanner
  filename string
  lineNumber int
  lineOffset int
}

type Token struct {
  Id int
  Literal string
  filename string
  lineNumber int
  lineOffset int
}

func (t *Token) ToString() string {
  name, ok := id2name[t.Id]
  if ok {
    return fmt.Sprintf("<Token:%s (%s:%d,%d) %q>", name, t.filename, t.lineNumber, t.lineOffset, t.Literal)
  } else {
    return fmt.Sprintf("<Token:%d (%s:%d,%d) %q>", t.Id, t.filename, t.lineNumber, t.lineOffset, t.Literal)
  }
}

func NewLexer(filename string, source string) *Lexer {
  return &Lexer {
    scanner: strscan.NewStringScanner(source),
    filename: filename,
    lineNumber: 0,
    lineOffset: 0,
  }
}

var keywords map[string]int = map[string]int {
  "void": VOID,
  "char": CHAR,
  "short": SHORT,
  "int": INT,
  "long": LONG,
  "struct": STRUCT,
  "union": UNION,
  "enum": ENUM,
  "static": STATIC,
  "extern": EXTERN,
  "const": CONST,
  "signed": SIGNED,
  "unsigned": UNSIGNED,
  "if": IF,
  "else": ELSE,
  "switch": SWITCH,
  "case": CASE,
  "default": DEFAULT,
  "while": WHILE,
  "do": DO,
  "for": FOR,
  "return": RETURN,
  "break": BREAK,
  "continue": CONTINUE,
  "goto": GOTO,
  "typedef": TYPEDEF,
  "import": IMPORT,
  "sizeof": SIZEOF,
}

func (self *Lexer) GetToken() (t *Token) {
  if self.scanner.IsEOS() {
    return nil
  }

  t = self.readSpaces()
  if t != nil {
    return t
  }

  t = self.readBlockComment()
  if t != nil {
    return t
  }
  t = self.readLineComment()
  if t != nil {
    return t
  }

  t = self.readKeyword()
  if t != nil {
    return t
  }

  t = self.readIdentifier()
  if t != nil {
    return t
  }

  t = self.readInteger()
  if t != nil {
    return t
  }

  t = self.readCharacter()
  if t != nil {
    return t
  }

  t = self.readString()
  if t != nil {
    return t
  }

  t = self.readOperator()
  if t != nil {
    return t
  }

  panic("lexer error")
}

func (self *Lexer) newToken(id int, literal string) (t *Token) {
  t = &Token {
    Id: id,
    Literal: literal,
    filename: self.filename,
    lineNumber: self.lineNumber,
    lineOffset: self.lineOffset,
  }

  self.lineNumber += strings.Count(literal, "\n")
  i := strings.LastIndex(literal, "\n")
  if i < 0 {
    self.lineOffset += len(literal)
  } else {
    self.lineOffset = len(literal[i:])
  }

  return t
}

func (self *Lexer) readBlockComment() *Token {
  s := self.scanner.Scan("/\\*")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("\\*/")
  if more == "" {
    panic("lexer error")
  }
  return self.newToken(BLOCK_COMMENT, s + more)
}

func (self *Lexer) readLineComment() *Token {
  s := self.scanner.Scan("//")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("(\n|\r\n|\r)")
  if more == "" {
    panic("lexer error")
  }
  return self.newToken(LINE_COMMENT, s + more)
}

func (self *Lexer) readSpaces() *Token {
  s := self.scanner.Scan("[ \t\n\r\f]+")
  if s == "" {
    return nil
  }
  return self.newToken(SPACES, s)
}

func (self *Lexer) readIdentifier() *Token {
  s := self.scanner.Scan("[_A-Za-z][_0-9A-Za-z]*")
  if s == "" {
    return nil
  }
  return self.newToken(IDENTIFIER, s)
}

func (self *Lexer) readInteger() *Token {
  s := self.scanner.Scan("([1-9][0-9]*U?L?|0[Xx][0-9A-Fa-f]+U?L?|0[0-7]*U?L?)")
  if s == "" {
    return nil
  }
  return self.newToken(INTEGER, s)
}

func (self *Lexer) readKeyword() *Token {
  for keyword, id := range keywords {
    s := self.scanner.Scan(regexp.QuoteMeta(keyword))
    if s != "" {
      return self.newToken(id, s)
    }
  }
  return nil
}

func (self *Lexer) readCharacter() *Token {
  s := self.scanner.Scan("'")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("'")
  if more == "" {
    panic("lexer error")
  }
  return self.newToken(CHARACTER, s + more)
}

func (self *Lexer) readString() *Token {
  s := self.scanner.Scan("\"")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("\"")
  if more == "" {
    panic("lexer error")
  }
  return self.newToken(STRING, s + more)
}

func (self *Lexer) readOperator() *Token {
  operators := []string {
    "<<=", ">>=", "...",
    "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=",
    "||", "&&", ">=", "<=", "==", "!=", "++", "--", ">>", "<<", "->",
    "=", ">", "<", ":", ";", "?", "{", "}", "(", ")", "+", "-", "!", "~",
    "*", "&", "|", "^", "&", "+", "-", "*", "/", "%", "[", "]", ".", ",",
  }
  for i := range operators {
    s := self.scanner.Scan(regexp.QuoteMeta(operators[i]))
    if s != "" {
      return self.newToken(OPERATOR, s)
    }
  }
  return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type rawImageClient interface {
	ListAliases() (shared.ImageAliases, error)
}

type imageClient struct {
	raw    rawImageClient
	config Config
}

// progressContext takes progress messages from LXD and just writes them to
// the associated logger at the given log level.
type progressContext struct {
	logger  loggo.Logger
	level   loggo.Level
	context string       // a format string that should take a single %s parameter
	forward func(string) // pass messages onward
}

func (p *progressContext) copyProgress(progress string) {
	msg := fmt.Sprintf(p.context, progress)
	p.logger.Logf(p.level, msg)
	if p.forward != nil {
		p.forward(msg)
	}
}

func (i *imageClient) EnsureImageExists(series string, copyProgressHandler func(string)) error {
	// TODO(jam) We should add Architecture in this information as well
	// TODO(jam) We should also update this for multiple locations to copy
	// from
	name := i.ImageNameForSeries(series)

	aliases, err := i.raw.ListAliases()
	if err != nil {
		return err
	}

	for _, alias := range aliases {
		if alias.Description == name {
			logger.Infof("found cached image %q = %s",
				alias.Description, alias.Target)
			return nil
		}
	}

	ubuntu, err := lxdClientForCloudImages(i.config)
	if err != nil {
		return err
	}

	client, ok := i.raw.(*lxd.Client)
	if !ok {
		return errors.Errorf("can't use a fake client as target")
	}
	adapter := &progressContext{
		logger:  logger,
		level:   loggo.INFO,
		context: fmt.Sprintf("copying image for %s from %s: %%s", series, ubuntu.BaseURL),
		forward: copyProgressHandler,
	}
	target := ubuntu.GetAlias(series)
	logger.Infof("found image from %s for %s = %s",
		ubuntu.BaseURL, series, target)
	return ubuntu.CopyImage(
		target, client, false, []string{name}, false,
		true, adapter.copyProgress)
}

// A common place to compute image names (alises) based on the series
func (i imageClient) ImageNameForSeries(series string) string {
	return "ubuntu-" + series
}
<commit_msg>use the name, not just the series<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type rawImageClient interface {
	ListAliases() (shared.ImageAliases, error)
}

type imageClient struct {
	raw    rawImageClient
	config Config
}

// progressContext takes progress messages from LXD and just writes them to
// the associated logger at the given log level.
type progressContext struct {
	logger  loggo.Logger
	level   loggo.Level
	context string       // a format string that should take a single %s parameter
	forward func(string) // pass messages onward
}

func (p *progressContext) copyProgress(progress string) {
	msg := fmt.Sprintf(p.context, progress)
	p.logger.Logf(p.level, msg)
	if p.forward != nil {
		p.forward(msg)
	}
}

func (i *imageClient) EnsureImageExists(series string, copyProgressHandler func(string)) error {
	// TODO(jam) We should add Architecture in this information as well
	// TODO(jam) We should also update this for multiple locations to copy
	// from
	name := i.ImageNameForSeries(series)

	aliases, err := i.raw.ListAliases()
	if err != nil {
		return err
	}

	for _, alias := range aliases {
		if alias.Description == name {
			logger.Infof("found cached image %q = %s",
				alias.Description, alias.Target)
			return nil
		}
	}

	ubuntu, err := lxdClientForCloudImages(i.config)
	if err != nil {
		return err
	}

	client, ok := i.raw.(*lxd.Client)
	if !ok {
		return errors.Errorf("can't use a fake client as target")
	}
	adapter := &progressContext{
		logger:  logger,
		level:   loggo.INFO,
		context: fmt.Sprintf("copying image for %s from %s: %%s", name, ubuntu.BaseURL),
		forward: copyProgressHandler,
	}
	target := ubuntu.GetAlias(series)
	logger.Infof("found image from %s for %s = %s",
		ubuntu.BaseURL, series, target)
	return ubuntu.CopyImage(
		target, client, false, []string{name}, false,
		true, adapter.copyProgress)
}

// A common place to compute image names (alises) based on the series
func (i imageClient) ImageNameForSeries(series string) string {
	return "ubuntu-" + series
}
<|endoftext|>


<commit_before>package revelswagger

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/url"

	"github.com/revel/revel"
)

var spec Specification
var router *revel.Router

func Init(path string, r *revel.Router) {
	// We need to load the JSON schema now
	fmt.Println("[SWAGGER]: Loading schema...")

	content, err := ioutil.ReadFile(path + "\\conf\\spec.json")

	if err != nil {
		fmt.Println("[SWAGGER]: Couldn't load spec.json.", err)
		return
	}

	err = json.Unmarshal(content, &spec)

	if err != nil {
		fmt.Println("[SWAGGER]: Error parsing schema file.", err)
	}

	router = r
}

func Filter(c *revel.Controller, fc []revel.Filter) {
	var route *revel.RouteMatch = router.Route(c.Request.Request)

	if route == nil {
		c.Result = c.NotFound("No matching route found: " + c.Request.RequestURI)
		return
	}

	if len(route.Params) == 0 {
		c.Params.Route = map[string][]string{}
	} else {
		c.Params.Route = route.Params
	}

	// Add the fixed parameters mapped by name.
	// TODO: Pre-calculate this mapping.
	for i, value := range route.FixedParams {
		if c.Params.Fixed == nil {
			c.Params.Fixed = make(url.Values)
		}
		if i < len(c.MethodType.Args) {
			arg := c.MethodType.Args[i]
			c.Params.Fixed.Set(arg.Name, value)
		} else {
			fmt.Println("Too many parameters to", route.Action, "trying to add", value)
			break
		}
	}

	leaf, _ := router.Tree.Find(treePath(c.Request.Method, c.Request.URL.Path))

	r := leaf.Value.(*revel.Route)

	method := spec.Paths[r.Path].Get

	if method == nil {
		c.Result = c.NotFound("No matching route found: " + c.Request.RequestURI)
		return
	}

	if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
		c.Result = c.NotFound(err.Error())
		return
	}

	// Action has been found & set, let's validate the parameters
	validateParameters(method.Parameters, c)

	if c.Validation.HasErrors() {
		var errors []string

		for _, e := range c.Validation.Errors {
			errors = append(errors, e.Message)
		}

		c.Result = c.RenderJson(map[string]interface{}{"errors": errors})
		return
	}

	fc[0](c, fc[1:])
}

func treePath(method, path string) string {
	if method == "*" {
		method = ":METHOD"
	}
	return "/" + method + path
}
<commit_msg>Added automatic reloading of spec file<commit_after>package revelswagger

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"

	"github.com/howeyc/fsnotify"
	"github.com/revel/revel"
)

var spec Specification
var router *revel.Router

func Init(path string, r *revel.Router) {
	// We need to load the JSON schema now
	fmt.Println("[SWAGGER]: Loading schema...")

	router = r

	loadSpecFile(path)

	go watchSpecFile(path)
}

func loadSpecFile(path string) {
	spec = Specification{}

	content, err := ioutil.ReadFile(path + "\\conf\\spec.json")

	if err != nil {
		fmt.Println("[SWAGGER]: Couldn't load spec.json.", err)
		return
	}

	err = json.Unmarshal(content, &spec)

	if err != nil {
		fmt.Println("[SWAGGER]: Error parsing schema file.", err)
	}
}

func watchSpecFile(path string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	done := make(chan bool)

	// Process events
	go func() {
		for {
			select {
			case <-watcher.Event:
				loadSpecFile(path)
			case err := <-watcher.Error:
				fmt.Println("[SWAGGER]: Watcher error:", err)
			}
		}
	}()

	err = watcher.Watch(path + "\\conf\\spec.json")

	if err != nil {
		fmt.Println("[SWAGGER]: Error watching spec file:", err)
	} else {
		fmt.Println("[SWAGGER]: Spec watcher initialized")
	}

	<-done

	/* ... do stuff ... */
	watcher.Close()
}

func Filter(c *revel.Controller, fc []revel.Filter) {
	var route *revel.RouteMatch = router.Route(c.Request.Request)

	if route == nil {
		c.Result = c.NotFound("No matching route found: " + c.Request.RequestURI)
		return
	}

	if len(route.Params) == 0 {
		c.Params.Route = map[string][]string{}
	} else {
		c.Params.Route = route.Params
	}

	// Add the fixed parameters mapped by name.
	// TODO: Pre-calculate this mapping.
	for i, value := range route.FixedParams {
		if c.Params.Fixed == nil {
			c.Params.Fixed = make(url.Values)
		}
		if i < len(c.MethodType.Args) {
			arg := c.MethodType.Args[i]
			c.Params.Fixed.Set(arg.Name, value)
		} else {
			fmt.Println("Too many parameters to", route.Action, "trying to add", value)
			break
		}
	}

	leaf, _ := router.Tree.Find(treePath(c.Request.Method, c.Request.URL.Path))

	r := leaf.Value.(*revel.Route)

	method := spec.Paths[r.Path].Get

	if method == nil {
		c.Result = c.NotFound("No matching route found: " + c.Request.RequestURI)
		return
	}

	if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
		c.Result = c.NotFound(err.Error())
		return
	}

	// Action has been found & set, let's validate the parameters
	validateParameters(method.Parameters, c)

	if c.Validation.HasErrors() {
		var errors []string

		for _, e := range c.Validation.Errors {
			errors = append(errors, e.Message)
		}

		c.Result = c.RenderJson(map[string]interface{}{"errors": errors})
		return
	}

	fc[0](c, fc[1:])
}

func treePath(method, path string) string {
	if method == "*" {
		method = ":METHOD"
	}
	return "/" + method + path
}
<|endoftext|>


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

// Package pprof serves via its HTTP server runtime profiling data
// in the format expected by the pprof visualization tool.
// For more information about pprof, see
// http://code.google.com/p/google-perftools/.
//
// The package is typically only imported for the side effect of
// registering its HTTP handlers.
// The handled paths all begin with /debug/pprof/.
//
// To use pprof, link this package into your program:
//	import _ "net/http/pprof"
//
// Then use the pprof tool to look at the heap profile:
//
//	go tool pprof http://localhost:6060/debug/pprof/heap
//
// Or to look at a 30-second CPU profile:
//
//	go tool pprof http://localhost:6060/debug/pprof/profile
//
// Or to look at the thread creation profile:
//
//	go tool pprof http://localhost:6060/debug/pprof/thread
//
package pprof

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"
)

func init() {
	http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
	http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
	http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap))
	http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
	http.Handle("/debug/pprof/thread", http.HandlerFunc(Thread))
}

// Cmdline responds with the running program's
// command line, with arguments separated by NUL bytes.
// The package initialization registers it as /debug/pprof/cmdline.
func Cmdline(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
}

// Heap responds with the pprof-formatted heap profile.
// The package initialization registers it as /debug/pprof/heap.
func Heap(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	pprof.WriteHeapProfile(w)
}

// Thread responds with the pprof-formatted thread creation profile.
// The package initialization registers it as /debug/pprof/thread.
func Thread(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	pprof.WriteThreadProfile(w)
}

// Profile responds with the pprof-formatted cpu profile.
// The package initialization registers it as /debug/pprof/profile.
func Profile(w http.ResponseWriter, r *http.Request) {
	sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64)
	if sec == 0 {
		sec = 30
	}

	// Set Content Type assuming StartCPUProfile will work,
	// because if it does it starts writing.
	w.Header().Set("Content-Type", "application/octet-stream")
	if err := pprof.StartCPUProfile(w); err != nil {
		// StartCPUProfile failed, so no writes yet.
		// Can change header back to text content
		// and send error code.
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
		return
	}
	time.Sleep(time.Duration(sec) * time.Second)
	pprof.StopCPUProfile()
}

// Symbol looks up the program counters listed in the request,
// responding with a table mapping program counters to function names.
// The package initialization registers it as /debug/pprof/symbol.
func Symbol(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")

	// We have to read the whole POST body before
	// writing any output.  Buffer the output here.
	var buf bytes.Buffer

	// We don't know how many symbols we have, but we
	// do have symbol information.  Pprof only cares whether
	// this number is 0 (no symbols available) or > 0.
	fmt.Fprintf(&buf, "num_symbols: 1\n")

	var b *bufio.Reader
	if r.Method == "POST" {
		b = bufio.NewReader(r.Body)
	} else {
		b = bufio.NewReader(strings.NewReader(r.URL.RawQuery))
	}

	for {
		word, err := b.ReadSlice('+')
		if err == nil {
			word = word[0 : len(word)-1] // trim +
		}
		pc, _ := strconv.ParseUint(string(word), 0, 64)
		if pc != 0 {
			f := runtime.FuncForPC(uintptr(pc))
			if f != nil {
				fmt.Fprintf(&buf, "%#x %s\n", pc, f.Name())
			}
		}

		// Wait until here to check for err; the last
		// symbol will have an err because it doesn't end in +.
		if err != nil {
			if err != io.EOF {
				fmt.Fprintf(&buf, "reading request: %v\n", err)
			}
			break
		}
	}

	w.Write(buf.Bytes())
}
<commit_msg>net/http/pprof: link to blog post<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package pprof serves via its HTTP server runtime profiling data
// in the format expected by the pprof visualization tool.
// For more information about pprof, see
// http://code.google.com/p/google-perftools/.
//
// The package is typically only imported for the side effect of
// registering its HTTP handlers.
// The handled paths all begin with /debug/pprof/.
//
// To use pprof, link this package into your program:
//	import _ "net/http/pprof"
//
// Then use the pprof tool to look at the heap profile:
//
//	go tool pprof http://localhost:6060/debug/pprof/heap
//
// Or to look at a 30-second CPU profile:
//
//	go tool pprof http://localhost:6060/debug/pprof/profile
//
// Or to look at the thread creation profile:
//
//	go tool pprof http://localhost:6060/debug/pprof/thread
//
// For a study of the facility in action, visit
//
//	http://blog.golang.org/2011/06/profiling-go-programs.html
//
package pprof

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"
)

func init() {
	http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
	http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
	http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap))
	http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
	http.Handle("/debug/pprof/thread", http.HandlerFunc(Thread))
}

// Cmdline responds with the running program's
// command line, with arguments separated by NUL bytes.
// The package initialization registers it as /debug/pprof/cmdline.
func Cmdline(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
}

// Heap responds with the pprof-formatted heap profile.
// The package initialization registers it as /debug/pprof/heap.
func Heap(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	pprof.WriteHeapProfile(w)
}

// Thread responds with the pprof-formatted thread creation profile.
// The package initialization registers it as /debug/pprof/thread.
func Thread(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	pprof.WriteThreadProfile(w)
}

// Profile responds with the pprof-formatted cpu profile.
// The package initialization registers it as /debug/pprof/profile.
func Profile(w http.ResponseWriter, r *http.Request) {
	sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64)
	if sec == 0 {
		sec = 30
	}

	// Set Content Type assuming StartCPUProfile will work,
	// because if it does it starts writing.
	w.Header().Set("Content-Type", "application/octet-stream")
	if err := pprof.StartCPUProfile(w); err != nil {
		// StartCPUProfile failed, so no writes yet.
		// Can change header back to text content
		// and send error code.
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
		return
	}
	time.Sleep(time.Duration(sec) * time.Second)
	pprof.StopCPUProfile()
}

// Symbol looks up the program counters listed in the request,
// responding with a table mapping program counters to function names.
// The package initialization registers it as /debug/pprof/symbol.
func Symbol(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")

	// We have to read the whole POST body before
	// writing any output.  Buffer the output here.
	var buf bytes.Buffer

	// We don't know how many symbols we have, but we
	// do have symbol information.  Pprof only cares whether
	// this number is 0 (no symbols available) or > 0.
	fmt.Fprintf(&buf, "num_symbols: 1\n")

	var b *bufio.Reader
	if r.Method == "POST" {
		b = bufio.NewReader(r.Body)
	} else {
		b = bufio.NewReader(strings.NewReader(r.URL.RawQuery))
	}

	for {
		word, err := b.ReadSlice('+')
		if err == nil {
			word = word[0 : len(word)-1] // trim +
		}
		pc, _ := strconv.ParseUint(string(word), 0, 64)
		if pc != 0 {
			f := runtime.FuncForPC(uintptr(pc))
			if f != nil {
				fmt.Fprintf(&buf, "%#x %s\n", pc, f.Name())
			}
		}

		// Wait until here to check for err; the last
		// symbol will have an err because it doesn't end in +.
		if err != nil {
			if err != io.EOF {
				fmt.Fprintf(&buf, "reading request: %v\n", err)
			}
			break
		}
	}

	w.Write(buf.Bytes())
}
<|endoftext|>


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

package netchan

import (
	"strings"
	"testing"
	"time"
)

const count = 10     // number of items in most tests
const closeCount = 5 // number of items when sender closes early

const base = 23

func exportSend(exp *Exporter, n int, t *testing.T) {
	ch := make(chan int)
	err := exp.Export("exportedSend", ch, Send)
	if err != nil {
		t.Fatal("exportSend:", err)
	}
	go func() {
		for i := 0; i < n; i++ {
			ch <- base+i
		}
		close(ch)
	}()
}

func exportReceive(exp *Exporter, t *testing.T) {
	ch := make(chan int)
	err := exp.Export("exportedRecv", ch, Recv)
	if err != nil {
		t.Fatal("exportReceive:", err)
	}
	for i := 0; i < count; i++ {
		v := <-ch
		if closed(ch) {
			if i != closeCount {
				t.Errorf("exportReceive expected close at %d; got one at %d\n", closeCount, i)
			}
			break
		}
		if v != base+i {
			t.Errorf("export Receive: bad value: expected %d+%d=%d; got %d", base, i, base+i, v)
		}
	}
}

func importSend(imp *Importer, n int, t *testing.T) {
	ch := make(chan int)
	err := imp.ImportNValues("exportedRecv", ch, Send, count)
	if err != nil {
		t.Fatal("importSend:", err)
	}
	go func() {
		for i := 0; i < n; i++ {
			ch <- base+i
		}
		close(ch)
	}()
}

func importReceive(imp *Importer, t *testing.T, done chan bool) {
	ch := make(chan int)
	err := imp.ImportNValues("exportedSend", ch, Recv, count)
	if err != nil {
		t.Fatal("importReceive:", err)
	}
	for i := 0; i < count; i++ {
		v := <-ch
		if closed(ch) {
			if i != closeCount {
				t.Errorf("importReceive expected close at %d; got one at %d\n", closeCount, i)
			}
			break
		}
		if v != 23+i {
			t.Errorf("importReceive: bad value: expected %%d+%d=%d; got %+d", base, i, base+i, v)
		}
	}
	if done != nil {
		done <- true
	}
}

func TestExportSendImportReceive(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	exportSend(exp, count, t)
	importReceive(imp, t, nil)
}

func TestExportReceiveImportSend(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	importSend(imp, count, t)
	exportReceive(exp, t)
}

func TestClosingExportSendImportReceive(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	exportSend(exp, closeCount, t)
	importReceive(imp, t, nil)
}

func TestClosingImportSendExportReceive(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	importSend(imp, closeCount, t)
	exportReceive(exp, t)
}

func TestErrorForIllegalChannel(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	// Now export a channel.
	ch := make(chan int, 1)
	err = exp.Export("aChannel", ch, Send)
	if err != nil {
		t.Fatal("export:", err)
	}
	ch <- 1234
	close(ch)
	// Now try to import a different channel.
	ch = make(chan int)
	err = imp.Import("notAChannel", ch, Recv)
	if err != nil {
		t.Fatal("import:", err)
	}
	// Expect an error now.  Start a timeout.
	timeout := make(chan bool, 1) // buffered so closure will not hang around.
	go func() {
		time.Sleep(10e9) // very long, to give even really slow machines a chance.
		timeout <- true
	}()
	select {
	case err = <-imp.Errors():
		if strings.Index(err.String(), "no such channel") < 0 {
			t.Errorf("wrong error for nonexistent channel:", err)
		}
	case <-timeout:
		t.Error("import of nonexistent channel did not receive an error")
	}
}

// Not a great test but it does at least invoke Drain.
func TestExportDrain(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	done := make(chan bool)
	go exportSend(exp, closeCount, t)
	go importReceive(imp, t, done)
	exp.Drain(0)
	<-done
}

// Not a great test but it does at least invoke Sync.
func TestExportSync(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	done := make(chan bool)
	go importReceive(imp, t, done)
	exportSend(exp, closeCount, t)
	exp.Sync(0)
	<-done
}

type value struct {
	i      int
	source string
}

// This test cross-connects a pair of exporter/importer pairs.
func TestCrossConnect(t *testing.T) {
	e1, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	i1, err := NewImporter("tcp", e1.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}

	e2, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	i2, err := NewImporter("tcp", e2.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}

	go crossExport(e1, e2, t)
	crossImport(i1, i2, t)
}

// Export side of cross-traffic.
func crossExport(e1, e2 *Exporter, t *testing.T) {
	s := make(chan value)
	err := e1.Export("exportedSend", s, Send)
	if err != nil {
		t.Fatal("exportSend:", err)
	}

	r := make(chan value)
	err = e2.Export("exportedReceive", r, Recv)
	if err != nil {
		t.Fatal("exportReceive:", err)
	}

	crossLoop("export", s, r, t)
}

// Import side of cross-traffic.
func crossImport(i1, i2 *Importer, t *testing.T) {
	s := make(chan value)
	err := i2.Import("exportedReceive", s, Send)
	if err != nil {
		t.Fatal("import of exportedReceive:", err)
	}

	r := make(chan value)
	err = i1.Import("exportedSend", r, Recv)
	if err != nil {
		t.Fatal("import of exported Send:", err)
	}

	crossLoop("import", s, r, t)
}

// Cross-traffic: send and receive 'count' numbers.
func crossLoop(name string, s, r chan value, t *testing.T) {
	for si, ri := 0, 0; si < count && ri < count; {
		select {
		case s <- value{si, name}:
			si++
		case v := <-r:
			if v.i != ri {
				t.Errorf("loop: bad value: expected %d, hello; got %+v", ri, v)
			}
			ri++
		}
	}
}
<commit_msg>netchan: fix unimportant typo in test error call.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package netchan

import (
	"strings"
	"testing"
	"time"
)

const count = 10     // number of items in most tests
const closeCount = 5 // number of items when sender closes early

const base = 23

func exportSend(exp *Exporter, n int, t *testing.T) {
	ch := make(chan int)
	err := exp.Export("exportedSend", ch, Send)
	if err != nil {
		t.Fatal("exportSend:", err)
	}
	go func() {
		for i := 0; i < n; i++ {
			ch <- base+i
		}
		close(ch)
	}()
}

func exportReceive(exp *Exporter, t *testing.T) {
	ch := make(chan int)
	err := exp.Export("exportedRecv", ch, Recv)
	if err != nil {
		t.Fatal("exportReceive:", err)
	}
	for i := 0; i < count; i++ {
		v := <-ch
		if closed(ch) {
			if i != closeCount {
				t.Errorf("exportReceive expected close at %d; got one at %d\n", closeCount, i)
			}
			break
		}
		if v != base+i {
			t.Errorf("export Receive: bad value: expected %d+%d=%d; got %d", base, i, base+i, v)
		}
	}
}

func importSend(imp *Importer, n int, t *testing.T) {
	ch := make(chan int)
	err := imp.ImportNValues("exportedRecv", ch, Send, count)
	if err != nil {
		t.Fatal("importSend:", err)
	}
	go func() {
		for i := 0; i < n; i++ {
			ch <- base+i
		}
		close(ch)
	}()
}

func importReceive(imp *Importer, t *testing.T, done chan bool) {
	ch := make(chan int)
	err := imp.ImportNValues("exportedSend", ch, Recv, count)
	if err != nil {
		t.Fatal("importReceive:", err)
	}
	for i := 0; i < count; i++ {
		v := <-ch
		if closed(ch) {
			if i != closeCount {
				t.Errorf("importReceive expected close at %d; got one at %d\n", closeCount, i)
			}
			break
		}
		if v != 23+i {
			t.Errorf("importReceive: bad value: expected %%d+%d=%d; got %+d", base, i, base+i, v)
		}
	}
	if done != nil {
		done <- true
	}
}

func TestExportSendImportReceive(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	exportSend(exp, count, t)
	importReceive(imp, t, nil)
}

func TestExportReceiveImportSend(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	importSend(imp, count, t)
	exportReceive(exp, t)
}

func TestClosingExportSendImportReceive(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	exportSend(exp, closeCount, t)
	importReceive(imp, t, nil)
}

func TestClosingImportSendExportReceive(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	importSend(imp, closeCount, t)
	exportReceive(exp, t)
}

func TestErrorForIllegalChannel(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	// Now export a channel.
	ch := make(chan int, 1)
	err = exp.Export("aChannel", ch, Send)
	if err != nil {
		t.Fatal("export:", err)
	}
	ch <- 1234
	close(ch)
	// Now try to import a different channel.
	ch = make(chan int)
	err = imp.Import("notAChannel", ch, Recv)
	if err != nil {
		t.Fatal("import:", err)
	}
	// Expect an error now.  Start a timeout.
	timeout := make(chan bool, 1) // buffered so closure will not hang around.
	go func() {
		time.Sleep(10e9) // very long, to give even really slow machines a chance.
		timeout <- true
	}()
	select {
	case err = <-imp.Errors():
		if strings.Index(err.String(), "no such channel") < 0 {
			t.Error("wrong error for nonexistent channel:", err)
		}
	case <-timeout:
		t.Error("import of nonexistent channel did not receive an error")
	}
}

// Not a great test but it does at least invoke Drain.
func TestExportDrain(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	done := make(chan bool)
	go exportSend(exp, closeCount, t)
	go importReceive(imp, t, done)
	exp.Drain(0)
	<-done
}

// Not a great test but it does at least invoke Sync.
func TestExportSync(t *testing.T) {
	exp, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	imp, err := NewImporter("tcp", exp.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}
	done := make(chan bool)
	go importReceive(imp, t, done)
	exportSend(exp, closeCount, t)
	exp.Sync(0)
	<-done
}

type value struct {
	i      int
	source string
}

// This test cross-connects a pair of exporter/importer pairs.
func TestCrossConnect(t *testing.T) {
	e1, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	i1, err := NewImporter("tcp", e1.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}

	e2, err := NewExporter("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal("new exporter:", err)
	}
	i2, err := NewImporter("tcp", e2.Addr().String())
	if err != nil {
		t.Fatal("new importer:", err)
	}

	go crossExport(e1, e2, t)
	crossImport(i1, i2, t)
}

// Export side of cross-traffic.
func crossExport(e1, e2 *Exporter, t *testing.T) {
	s := make(chan value)
	err := e1.Export("exportedSend", s, Send)
	if err != nil {
		t.Fatal("exportSend:", err)
	}

	r := make(chan value)
	err = e2.Export("exportedReceive", r, Recv)
	if err != nil {
		t.Fatal("exportReceive:", err)
	}

	crossLoop("export", s, r, t)
}

// Import side of cross-traffic.
func crossImport(i1, i2 *Importer, t *testing.T) {
	s := make(chan value)
	err := i2.Import("exportedReceive", s, Send)
	if err != nil {
		t.Fatal("import of exportedReceive:", err)
	}

	r := make(chan value)
	err = i1.Import("exportedSend", r, Recv)
	if err != nil {
		t.Fatal("import of exported Send:", err)
	}

	crossLoop("import", s, r, t)
}

// Cross-traffic: send and receive 'count' numbers.
func crossLoop(name string, s, r chan value, t *testing.T) {
	for si, ri := 0, 0; si < count && ri < count; {
		select {
		case s <- value{si, name}:
			si++
		case v := <-r:
			if v.i != ri {
				t.Errorf("loop: bad value: expected %d, hello; got %+v", ri, v)
			}
			ri++
		}
	}
}
<|endoftext|>