<commit_before>package router

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"net"
	"time"
)

const (
	InitialInterval = 5 * time.Second
	MaxInterval     = 10 * time.Minute
)

type ConnectionMaker struct {
	ourself        *LocalPeer
	peers          *Peers
	targets        map[string]*Target
	cmdLineAddress map[string]bool
	actionChan     chan<- ConnectionMakerAction
}

// Information about an address where we may find a peer
type Target struct {
	attempting  bool          // are we currently attempting to connect there?
	tryAfter    time.Time     // next time to try this address
	tryInterval time.Duration // backoff time on next failure
}

type ConnectionMakerAction func() bool

func NewConnectionMaker(ourself *LocalPeer, peers *Peers) *ConnectionMaker {
	return &ConnectionMaker{
		ourself:        ourself,
		peers:          peers,
		cmdLineAddress: make(map[string]bool),
		targets:        make(map[string]*Target)}
}

func (cm *ConnectionMaker) Start() {
	actionChan := make(chan ConnectionMakerAction, ChannelSize)
	cm.actionChan = actionChan
	go cm.queryLoop(actionChan)
}

func (cm *ConnectionMaker) InitiateConnection(address string) {
	cm.actionChan <- func() bool {
		cm.cmdLineAddress[NormalisePeerAddr(address)] = true
		return true
	}
}

func (cm *ConnectionMaker) ForgetConnection(address string) {
	cm.actionChan <- func() bool {
		delete(cm.cmdLineAddress, NormalisePeerAddr(address))
		return true
	}
}

func (cm *ConnectionMaker) ConnectionTerminated(address string) {
	cm.actionChan <- func() bool {
		if target, found := cm.targets[address]; found {
			target.attempting = false
			target.tryAfter, target.tryInterval = tryAfter(target.tryInterval)
		}
		return true
	}
}

func (cm *ConnectionMaker) Refresh() {
	cm.actionChan <- func() bool { return true }
}

func (cm *ConnectionMaker) String() string {
	resultChan := make(chan string, 0)
	cm.actionChan <- func() bool {
		var buf bytes.Buffer
		for address, target := range cm.targets {
			var fmtStr string
			if target.attempting {
				fmtStr = "%s (trying since %v)\n"
			} else {
				fmtStr = "%s (next try at %v)\n"
			}
			buf.WriteString(fmt.Sprintf(fmtStr, address, target.tryAfter))
		}
		resultChan <- buf.String()
		return false
	}
	return <-resultChan
}

func (cm *ConnectionMaker) queryLoop(actionChan <-chan ConnectionMakerAction) {
	timer := time.NewTimer(MaxDuration)
	run := func() { timer.Reset(cm.checkStateAndAttemptConnections()) }
	for {
		select {
		case action := <-actionChan:
			if action() {
				run()
			}
		case <-timer.C:
			run()
		}
	}
}

func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
	validTarget := make(map[string]bool)

	// copy the set of things we are connected to, so we can access them without locking
	ourConnectedPeers := make(map[PeerName]bool)
	ourConnectedTargets := make(map[string]bool)
	for _, conn := range cm.ourself.Connections() {
		ourConnectedPeers[conn.Remote().Name] = true
		ourConnectedTargets[conn.RemoteTCPAddr()] = true
	}

	addTarget := func(address string) {
		if !ourConnectedTargets[address] {
			validTarget[address] = true
			cm.addTarget(address)
		}
	}

	// Add command-line targets that are not connected
	for address, _ := range cm.cmdLineAddress {
		addTarget(address)
	}

	// Add targets for peers that someone else is connected to, but we
	// aren't
	cm.peers.ForEach(func(name PeerName, peer *Peer) {
		for _, conn := range peer.Connections() {
			otherPeer := conn.Remote().Name
			if otherPeer == cm.ourself.Name || ourConnectedPeers[otherPeer] {
				return
			}
			address := conn.RemoteTCPAddr()
			// try both portnumber of connection and standard port.  Don't use remote side of inbound connection.
			if conn.Outbound() {
				addTarget(address)
			}
			if host, _, err := net.SplitHostPort(address); err == nil {
				addTarget(NormalisePeerAddr(host))
			}
		}
	})

	now := time.Now() // make sure we catch items just added
	after := MaxDuration
	for address, target := range cm.targets {
		if ourConnectedTargets[address] {
			delete(cm.targets, address)
			continue
		}
		if target.attempting {
			continue
		}
		if !validTarget[address] {
			delete(cm.targets, address)
			continue
		}
		switch duration := target.tryAfter.Sub(now); {
		case duration <= 0:
			target.attempting = true
			go cm.attemptConnection(address, cm.cmdLineAddress[address])
		case duration < after:
			after = duration
		}
	}
	return after
}

func (cm *ConnectionMaker) addTarget(address string) {
	if _, found := cm.targets[address]; !found {
		target := &Target{}
		target.tryAfter, target.tryInterval = tryImmediately()
		cm.targets[address] = target
	}
}

func (cm *ConnectionMaker) attemptConnection(address string, acceptNewPeer bool) {
	log.Printf("->[%s] attempting connection\n", address)
	if err := cm.ourself.CreateConnection(address, acceptNewPeer); err != nil {
		log.Printf("->[%s] error during connection attempt: %v\n", address, err)
		cm.ConnectionTerminated(address)
	}
}

func tryImmediately() (time.Time, time.Duration) {
	interval := time.Duration(rand.Int63n(int64(InitialInterval)))
	return time.Now(), interval
}

func tryAfter(interval time.Duration) (time.Time, time.Duration) {
	interval += time.Duration(rand.Int63n(int64(interval)))
	if interval > MaxInterval {
		interval = MaxInterval
	}
	return time.Now().Add(interval), interval
}
<commit_msg>Don't reset timer when forgetting an address<commit_after>package router

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"net"
	"time"
)

const (
	InitialInterval = 5 * time.Second
	MaxInterval     = 10 * time.Minute
)

type ConnectionMaker struct {
	ourself        *LocalPeer
	peers          *Peers
	targets        map[string]*Target
	cmdLineAddress map[string]bool
	actionChan     chan<- ConnectionMakerAction
}

// Information about an address where we may find a peer
type Target struct {
	attempting  bool          // are we currently attempting to connect there?
	tryAfter    time.Time     // next time to try this address
	tryInterval time.Duration // backoff time on next failure
}

type ConnectionMakerAction func() bool

func NewConnectionMaker(ourself *LocalPeer, peers *Peers) *ConnectionMaker {
	return &ConnectionMaker{
		ourself:        ourself,
		peers:          peers,
		cmdLineAddress: make(map[string]bool),
		targets:        make(map[string]*Target)}
}

func (cm *ConnectionMaker) Start() {
	actionChan := make(chan ConnectionMakerAction, ChannelSize)
	cm.actionChan = actionChan
	go cm.queryLoop(actionChan)
}

func (cm *ConnectionMaker) InitiateConnection(address string) {
	cm.actionChan <- func() bool {
		cm.cmdLineAddress[NormalisePeerAddr(address)] = true
		return true
	}
}

func (cm *ConnectionMaker) ForgetConnection(address string) {
	cm.actionChan <- func() bool {
		delete(cm.cmdLineAddress, NormalisePeerAddr(address))
		return false
	}
}

func (cm *ConnectionMaker) ConnectionTerminated(address string) {
	cm.actionChan <- func() bool {
		if target, found := cm.targets[address]; found {
			target.attempting = false
			target.tryAfter, target.tryInterval = tryAfter(target.tryInterval)
		}
		return true
	}
}

func (cm *ConnectionMaker) Refresh() {
	cm.actionChan <- func() bool { return true }
}

func (cm *ConnectionMaker) String() string {
	resultChan := make(chan string, 0)
	cm.actionChan <- func() bool {
		var buf bytes.Buffer
		for address, target := range cm.targets {
			var fmtStr string
			if target.attempting {
				fmtStr = "%s (trying since %v)\n"
			} else {
				fmtStr = "%s (next try at %v)\n"
			}
			buf.WriteString(fmt.Sprintf(fmtStr, address, target.tryAfter))
		}
		resultChan <- buf.String()
		return false
	}
	return <-resultChan
}

func (cm *ConnectionMaker) queryLoop(actionChan <-chan ConnectionMakerAction) {
	timer := time.NewTimer(MaxDuration)
	run := func() { timer.Reset(cm.checkStateAndAttemptConnections()) }
	for {
		select {
		case action := <-actionChan:
			if action() {
				run()
			}
		case <-timer.C:
			run()
		}
	}
}

func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
	validTarget := make(map[string]bool)

	// copy the set of things we are connected to, so we can access them without locking
	ourConnectedPeers := make(map[PeerName]bool)
	ourConnectedTargets := make(map[string]bool)
	for _, conn := range cm.ourself.Connections() {
		ourConnectedPeers[conn.Remote().Name] = true
		ourConnectedTargets[conn.RemoteTCPAddr()] = true
	}

	addTarget := func(address string) {
		if !ourConnectedTargets[address] {
			validTarget[address] = true
			cm.addTarget(address)
		}
	}

	// Add command-line targets that are not connected
	for address, _ := range cm.cmdLineAddress {
		addTarget(address)
	}

	// Add targets for peers that someone else is connected to, but we
	// aren't
	cm.peers.ForEach(func(name PeerName, peer *Peer) {
		for _, conn := range peer.Connections() {
			otherPeer := conn.Remote().Name
			if otherPeer == cm.ourself.Name || ourConnectedPeers[otherPeer] {
				return
			}
			address := conn.RemoteTCPAddr()
			// try both portnumber of connection and standard port.  Don't use remote side of inbound connection.
			if conn.Outbound() {
				addTarget(address)
			}
			if host, _, err := net.SplitHostPort(address); err == nil {
				addTarget(NormalisePeerAddr(host))
			}
		}
	})

	now := time.Now() // make sure we catch items just added
	after := MaxDuration
	for address, target := range cm.targets {
		if ourConnectedTargets[address] {
			delete(cm.targets, address)
			continue
		}
		if target.attempting {
			continue
		}
		if !validTarget[address] {
			delete(cm.targets, address)
			continue
		}
		switch duration := target.tryAfter.Sub(now); {
		case duration <= 0:
			target.attempting = true
			go cm.attemptConnection(address, cm.cmdLineAddress[address])
		case duration < after:
			after = duration
		}
	}
	return after
}

func (cm *ConnectionMaker) addTarget(address string) {
	if _, found := cm.targets[address]; !found {
		target := &Target{}
		target.tryAfter, target.tryInterval = tryImmediately()
		cm.targets[address] = target
	}
}

func (cm *ConnectionMaker) attemptConnection(address string, acceptNewPeer bool) {
	log.Printf("->[%s] attempting connection\n", address)
	if err := cm.ourself.CreateConnection(address, acceptNewPeer); err != nil {
		log.Printf("->[%s] error during connection attempt: %v\n", address, err)
		cm.ConnectionTerminated(address)
	}
}

func tryImmediately() (time.Time, time.Duration) {
	interval := time.Duration(rand.Int63n(int64(InitialInterval)))
	return time.Now(), interval
}

func tryAfter(interval time.Duration) (time.Time, time.Duration) {
	interval += time.Duration(rand.Int63n(int64(interval)))
	if interval > MaxInterval {
		interval = MaxInterval
	}
	return time.Now().Add(interval), interval
}
<|endoftext|>


<commit_before>// +build !windows
// +build !plan9

package tty

import (
	"bufio"
	"os"
	"syscall"
	"unsafe"
)

type TTY struct {
	in      *os.File
	bin     *bufio.Reader
	out     *os.File
	termios syscall.Termios
}

func open() (*TTY, error) {
	tty := new(TTY)

	in, err := os.Open("/dev/tty")
	if err != nil {
		return nil, err
	}
	tty.in = in
	tty.bin = bufio.NewReader(in)

	out, err := os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
	if err != nil {
		return nil, err
	}
	tty.out = out

	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0); err != 0 {
		return nil, err
	}
	newios := tty.termios
	newios.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
	newios.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newios)), 0, 0, 0); err != 0 {
		return nil, err
	}
	return tty, nil
}

func (tty *TTY) buffered() bool {
	return tty.bin.Buffered() > 0
}

func (tty *TTY) readRune() (rune, error) {
	r, _, err := tty.bin.ReadRune()
	return r, err
}

func (tty *TTY) close() error {
	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0)
	return err
}

func (tty *TTY) size() (int, int, error) {
	var dim [4]uint16
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 {
		return -1, -1, err
	}
	return int(dim[1]), int(dim[0]), nil
}

func (tty *TTY) input() *os.File {
	return tty.in
}

func (tty *TTY) output() *os.File {
	return tty.out
}
<commit_msg>signal should not be handled<commit_after>// +build !windows
// +build !plan9

package tty

import (
	"bufio"
	"os"
	"syscall"
	"unsafe"
)

type TTY struct {
	in      *os.File
	bin     *bufio.Reader
	out     *os.File
	termios syscall.Termios
}

func open() (*TTY, error) {
	tty := new(TTY)

	in, err := os.Open("/dev/tty")
	if err != nil {
		return nil, err
	}
	tty.in = in
	tty.bin = bufio.NewReader(in)

	out, err := os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
	if err != nil {
		return nil, err
	}
	tty.out = out

	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0); err != 0 {
		return nil, err
	}
	newios := tty.termios
	newios.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
	newios.Lflag &^= syscall.ECHO | syscall.ICANON /*| syscall.ISIG*/
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newios)), 0, 0, 0); err != 0 {
		return nil, err
	}
	return tty, nil
}

func (tty *TTY) buffered() bool {
	return tty.bin.Buffered() > 0
}

func (tty *TTY) readRune() (rune, error) {
	r, _, err := tty.bin.ReadRune()
	return r, err
}

func (tty *TTY) close() error {
	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0)
	return err
}

func (tty *TTY) size() (int, int, error) {
	var dim [4]uint16
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 {
		return -1, -1, err
	}
	return int(dim[1]), int(dim[0]), nil
}

func (tty *TTY) input() *os.File {
	return tty.in
}

func (tty *TTY) output() *os.File {
	return tty.out
}
<|endoftext|>


<commit_before>package pack

import (
	"strings"
	. "testing"
)

func TestParse(t *T) {
	t.Parallel()
	var tests = []struct {
		Input  string
		Output Version
		Error  string
	}{
		{``, Version{}, `empty`},
		{`2`, Version{}, `form`},
		{`a.2`, Version{}, `form`},
		{`a.2.5`, Version{}, `form`},
		{`!==4.2.1`, Version{}, `form`},
		{`!=>=4.2.1`, Version{}, `form`},
		{`2.1.3`, Version{0, 2, 1, 3}, ``},
		{`4.2.1`, Version{Equal, 4, 2, 1}, ``},
		{`=4.2.1`, Version{Equal, 4, 2, 1}, ``},
		{`!=4.2.1`, Version{NotEqual, 4, 2, 1}, ``},
		{`>4.2.1`, Version{GreaterThan, 4, 2, 1}, ``},
		{`<4.2.1`, Version{LessThan, 4, 2, 1}, ``},
		{`>=4.2.1`, Version{GreaterEqual, 4, 2, 1}, ``},
		{`<=4.2.1`, Version{LessEqual, 4, 2, 1}, ``},
		{`~>4.2.1`, Version{ApproxGreater, 4, 2, 1}, ``},
	}

	for _, test := range tests {
		out, err := ParseVersion(test.Input)
		if err != nil {
			if len(test.Error) == 0 {
				t.Error(test)
				t.Error(test, "had unexpected error:", err)
			} else if !strings.Contains(err.Error(), test.Error) {
				t.Error(test, "expected error message like:",
					test.Error, "got:", err)
			}
		}

		if out != test.Output {
			t.Error("Expected:", out, "to be equal to:", test.Output)
		}
	}
}

func TestCompare(t *T) {
	t.Parallel()
	var tests = []struct {
		Base      Version
		Condition Version
		Result    bool
	}{
		// Base case
		{Version{0, 1, 0, 0}, Version{0, 1, 0, 0}, true},

		// =
		{Version{0, 1, 0, 0}, Version{Equal, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{Equal, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{Equal, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{Equal, 2, 0, 0}, false},

		// !=
		{Version{0, 1, 0, 0}, Version{NotEqual, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{NotEqual, 1, 0, 1}, true},
		{Version{0, 1, 0, 0}, Version{NotEqual, 1, 1, 0}, true},
		{Version{0, 1, 0, 0}, Version{NotEqual, 2, 0, 0}, true},

		// >
		{Version{0, 1, 0, 1}, Version{GreaterThan, 1, 0, 0}, true},
		{Version{0, 1, 1, 0}, Version{GreaterThan, 1, 0, 0}, true},
		{Version{0, 2, 0, 0}, Version{GreaterThan, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 2, 0, 0}, false},

		// <
		{Version{0, 1, 0, 1}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 1, 1, 0}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 2, 0, 0}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{LessThan, 1, 0, 1}, true},
		{Version{0, 1, 0, 0}, Version{LessThan, 1, 1, 0}, true},
		{Version{0, 1, 0, 0}, Version{LessThan, 2, 0, 0}, true},

		// >=
		{Version{0, 1, 0, 1}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 1, 1, 0}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 2, 0, 0}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 2, 0, 0}, false},

		// <=
		{Version{0, 1, 0, 1}, Version{LessEqual, 1, 0, 0}, false},
		{Version{0, 1, 1, 0}, Version{LessEqual, 1, 0, 0}, false},
		{Version{0, 2, 0, 0}, Version{LessEqual, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{LessEqual, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{LessEqual, 1, 0, 1}, true},
		{Version{0, 1, 0, 0}, Version{LessEqual, 1, 1, 0}, true},
		{Version{0, 1, 0, 0}, Version{LessEqual, 2, 0, 0}, true},

		// ~>
		{Version{0, 1, 0, 1}, Version{ApproxGreater, 1, 0, 0}, true},
		{Version{0, 1, 1, 0}, Version{ApproxGreater, 1, 0, 0}, true},
		{Version{0, 2, 0, 0}, Version{ApproxGreater, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 2, 0, 0}, false},
	}

	for _, test := range tests {
		res := test.Base.Compare(test.Condition)

		if res != test.Result {
			t.Error(test, "expected:", res, "to be equal to:", test.Result)
		}
	}
}

func TestVersion_String(t *T) {
	t.Parallel()
	var tests = []struct {
		Version  Version
		Expected string
	}{
		{Version{Equal, 1, 2, 3}, `1.2.3`},
		{Version{NotEqual, 1, 2, 3}, `!=1.2.3`},
		{Version{GreaterThan, 1, 2, 3}, `>1.2.3`},
		{Version{LessThan, 1, 2, 3}, `<1.2.3`},
		{Version{GreaterEqual, 1, 2, 3}, `>=1.2.3`},
		{Version{LessEqual, 1, 2, 3}, `<=1.2.3`},
		{Version{ApproxGreater, 1, 2, 3}, `~>1.2.3`},
	}

	for _, test := range tests {
		if s := test.Version.String(); s != test.Expected {
			t.Error(test, "expected:", s, "to be equal to:", test.Expected)
		}
	}
}

func TestGetYAML(t *T) {
	t.Parallel()
	v := Version{NotEqual, 1, 2, 3}
	_, value := v.GetYAML()
	if s, ok := value.(string); !ok {
		t.Error("It should return a string type.")
	} else if s != "!=1.2.3" {
		t.Error("It's not returning the correct string.")
	}
}

func TestSetYAML(t *T) {
	t.Parallel()
	var v Version
	if v.SetYAML("", "fail") {
		t.Error("Expecting failure.")
	}
	if v.SetYAML("", 10) {
		t.Error("Expecting failure.")
	}
	success := v.SetYAML("", ">=1.2.3")
	if !success {
		t.Error("Expecting success.")
	}
	comp := Version{Equal, 1, 2, 3}
	if !v.Compare(comp) {
		t.Error("Expected:", v, "to match", comp)
	}
}
<commit_msg>Fix test method names.<commit_after>package pack

import (
	"strings"
	. "testing"
)

func TestParse(t *T) {
	t.Parallel()
	var tests = []struct {
		Input  string
		Output Version
		Error  string
	}{
		{``, Version{}, `empty`},
		{`2`, Version{}, `form`},
		{`a.2`, Version{}, `form`},
		{`a.2.5`, Version{}, `form`},
		{`!==4.2.1`, Version{}, `form`},
		{`!=>=4.2.1`, Version{}, `form`},
		{`2.1.3`, Version{0, 2, 1, 3}, ``},
		{`4.2.1`, Version{Equal, 4, 2, 1}, ``},
		{`=4.2.1`, Version{Equal, 4, 2, 1}, ``},
		{`!=4.2.1`, Version{NotEqual, 4, 2, 1}, ``},
		{`>4.2.1`, Version{GreaterThan, 4, 2, 1}, ``},
		{`<4.2.1`, Version{LessThan, 4, 2, 1}, ``},
		{`>=4.2.1`, Version{GreaterEqual, 4, 2, 1}, ``},
		{`<=4.2.1`, Version{LessEqual, 4, 2, 1}, ``},
		{`~>4.2.1`, Version{ApproxGreater, 4, 2, 1}, ``},
	}

	for _, test := range tests {
		out, err := ParseVersion(test.Input)
		if err != nil {
			if len(test.Error) == 0 {
				t.Error(test)
				t.Error(test, "had unexpected error:", err)
			} else if !strings.Contains(err.Error(), test.Error) {
				t.Error(test, "expected error message like:",
					test.Error, "got:", err)
			}
		}

		if out != test.Output {
			t.Error("Expected:", out, "to be equal to:", test.Output)
		}
	}
}

func TestCompare(t *T) {
	t.Parallel()
	var tests = []struct {
		Base      Version
		Condition Version
		Result    bool
	}{
		// Base case
		{Version{0, 1, 0, 0}, Version{0, 1, 0, 0}, true},

		// =
		{Version{0, 1, 0, 0}, Version{Equal, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{Equal, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{Equal, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{Equal, 2, 0, 0}, false},

		// !=
		{Version{0, 1, 0, 0}, Version{NotEqual, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{NotEqual, 1, 0, 1}, true},
		{Version{0, 1, 0, 0}, Version{NotEqual, 1, 1, 0}, true},
		{Version{0, 1, 0, 0}, Version{NotEqual, 2, 0, 0}, true},

		// >
		{Version{0, 1, 0, 1}, Version{GreaterThan, 1, 0, 0}, true},
		{Version{0, 1, 1, 0}, Version{GreaterThan, 1, 0, 0}, true},
		{Version{0, 2, 0, 0}, Version{GreaterThan, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{GreaterThan, 2, 0, 0}, false},

		// <
		{Version{0, 1, 0, 1}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 1, 1, 0}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 2, 0, 0}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{LessThan, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{LessThan, 1, 0, 1}, true},
		{Version{0, 1, 0, 0}, Version{LessThan, 1, 1, 0}, true},
		{Version{0, 1, 0, 0}, Version{LessThan, 2, 0, 0}, true},

		// >=
		{Version{0, 1, 0, 1}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 1, 1, 0}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 2, 0, 0}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{GreaterEqual, 2, 0, 0}, false},

		// <=
		{Version{0, 1, 0, 1}, Version{LessEqual, 1, 0, 0}, false},
		{Version{0, 1, 1, 0}, Version{LessEqual, 1, 0, 0}, false},
		{Version{0, 2, 0, 0}, Version{LessEqual, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{LessEqual, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{LessEqual, 1, 0, 1}, true},
		{Version{0, 1, 0, 0}, Version{LessEqual, 1, 1, 0}, true},
		{Version{0, 1, 0, 0}, Version{LessEqual, 2, 0, 0}, true},

		// ~>
		{Version{0, 1, 0, 1}, Version{ApproxGreater, 1, 0, 0}, true},
		{Version{0, 1, 1, 0}, Version{ApproxGreater, 1, 0, 0}, true},
		{Version{0, 2, 0, 0}, Version{ApproxGreater, 1, 0, 0}, false},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 1, 0, 0}, true},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 1, 0, 1}, false},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 1, 1, 0}, false},
		{Version{0, 1, 0, 0}, Version{ApproxGreater, 2, 0, 0}, false},
	}

	for _, test := range tests {
		res := test.Base.Compare(test.Condition)

		if res != test.Result {
			t.Error(test, "expected:", res, "to be equal to:", test.Result)
		}
	}
}

func TestVersion_String(t *T) {
	t.Parallel()
	var tests = []struct {
		Version  Version
		Expected string
	}{
		{Version{Equal, 1, 2, 3}, `1.2.3`},
		{Version{NotEqual, 1, 2, 3}, `!=1.2.3`},
		{Version{GreaterThan, 1, 2, 3}, `>1.2.3`},
		{Version{LessThan, 1, 2, 3}, `<1.2.3`},
		{Version{GreaterEqual, 1, 2, 3}, `>=1.2.3`},
		{Version{LessEqual, 1, 2, 3}, `<=1.2.3`},
		{Version{ApproxGreater, 1, 2, 3}, `~>1.2.3`},
	}

	for _, test := range tests {
		if s := test.Version.String(); s != test.Expected {
			t.Error(test, "expected:", s, "to be equal to:", test.Expected)
		}
	}
}

func TestVersion_GetYAML(t *T) {
	t.Parallel()
	v := Version{NotEqual, 1, 2, 3}
	_, value := v.GetYAML()
	if s, ok := value.(string); !ok {
		t.Error("It should return a string type.")
	} else if s != "!=1.2.3" {
		t.Error("It's not returning the correct string.")
	}
}

func TestVersion_SetYAML(t *T) {
	t.Parallel()
	var v Version
	if v.SetYAML("", "fail") {
		t.Error("Expecting failure.")
	}
	if v.SetYAML("", 10) {
		t.Error("Expecting failure.")
	}
	success := v.SetYAML("", ">=1.2.3")
	if !success {
		t.Error("Expecting success.")
	}
	comp := Version{Equal, 1, 2, 3}
	if !v.Compare(comp) {
		t.Error("Expected:", v, "to match", comp)
	}
}
<|endoftext|>


<commit_before>package tko

import (
	"encoding/json"
	"fmt"

	"github.com/MediaMath/keryxlib/message"
)

//ConditionDefinition wraps around the condition and allows it to be parsed generically.  Only 1 of the fields will be returned if multiple exist
type ConditionDefinition struct {
	IsTransaction *TransactionIDMatches  `json:"transaction_is"`
	HasMessage    *HasMessage            `json:"has_message"`
	Not           *ConditionDefinition   `json:"not"`
	AnyOf         *[]ConditionDefinition `json:"any_of"`
	AllOf         *[]ConditionDefinition `json:"all_of"`
}

func ReadConditionFromJSON(jsonStr string) (Condition, error) {
	var parsed ConditionDefinition
	err := json.Unmarshal([]byte(jsonStr), &parsed)

	if err != nil {
		return nil, err
	}

	return conditionFromDefinition(parsed)
}

func conditionFromDefinition(parsed ConditionDefinition) (Condition, error) {
	if parsed.IsTransaction != nil {
		return parsed.IsTransaction, nil
	}

	if parsed.HasMessage != nil {
		return parsed.HasMessage, nil
	}

	if parsed.Not != nil {
		cond, err := conditionFromDefinition(*parsed.Not)
		if err != nil {
			return nil, err
		}
		return &Not{cond}, nil
	}

	if parsed.AnyOf != nil {
		conditions := []Condition{}
		for _, definition := range *parsed.AnyOf {
			cond, err := conditionFromDefinition(definition)
			if err != nil {
				return nil, err
			}
			conditions = append(conditions, cond)
		}

		return AnyOf(conditions), nil
	}

	if parsed.AllOf != nil {
		conditions := []Condition{}
		for _, definition := range *parsed.AllOf {
			cond, err := conditionFromDefinition(definition)
			if err != nil {
				return nil, err
			}
			conditions = append(conditions, cond)
		}

		return AllOf(conditions), nil
	}

	return nil, fmt.Errorf("Unknown condition: %v", parsed)
}

//Not will return the inverse of the underlying condition.
type Not struct {
	condition Condition
}

func (c *Not) Check(txn *message.Transaction) bool {
	orig := c.condition.Check(txn)
	return !orig
}

//AnyOf will return true if any of its underlying Conditions return true. Logical Or.
type AnyOf []Condition

func (c AnyOf) Check(txn *message.Transaction) bool {
	if len(c) == 0 {
		return false
	}

	for _, condition := range c {
		if condition.Check(txn) {
			return true
		}
	}

	return false
}

//AllOf will return true if all of its underlying Conditions return true. Logical And.
type AllOf []Condition

func (c AllOf) Check(txn *message.Transaction) bool {
	if len(c) == 0 {
		return true
	}

	for _, condition := range c {
		if !condition.Check(txn) {
			return false
		}
	}

	return true
}

//TransactionIDMatches will match a specific TransactionID
type TransactionIDMatches struct {
	TransactionID uint32 `json:"id"`
}

func (c *TransactionIDMatches) Check(txn *message.Transaction) bool {
	return c.TransactionID == txn.TransactionID
}

//HasMessage will match if any message in the transaction matches the provided message.
type HasMessage struct {
	Type          *message.Type          `json:"type"`
	DatabaseName  *string                `json:"database_name"`
	Namespace     *string                `json:"namespace"`
	Relation      *string                `json:"relation"`
	TupleID       *string                `json:"tuple_id"`
	PrevTupleID   *string                `json:"prev_tuple_id"`
	FieldsMatch   map[string]interface{} `json:"fields_match"`
	MissingFields *bool                  `json:"missing_fields"`
}

func (c *HasMessage) Check(txn *message.Transaction) bool {
	for _, msg := range txn.Messages {
		if c.checkMessage(msg) {
			return true
		}
	}

	return false
}

func (c *HasMessage) checkMessage(m message.Message) bool {
	chk := checkable{c, m}
	return chk.typeMatches() &&
		chk.databaseNameMatches() &&
		chk.namespaceMatches() &&
		chk.relationMatches() &&
		chk.tupleIDMatches() &&
		chk.prevTupleIDMatches() &&
		chk.missingFields() &&
		chk.fieldsMatch()
}

type checkable struct {
	cond *HasMessage
	msg  message.Message
}

func (c checkable) missingFields() bool {
	return c.cond.MissingFields == nil || (c.msg.Type != message.DeleteMessage && (len(c.msg.Fields) == 0) == *c.cond.MissingFields)
}

func (c checkable) typeMatches() bool {
	return c.cond.Type == nil || c.msg.Type == *c.cond.Type
}

func (c checkable) databaseNameMatches() bool {
	return c.cond.DatabaseName == nil || c.msg.DatabaseName == *c.cond.DatabaseName
}

func (c checkable) namespaceMatches() bool {
	return c.cond.Namespace == nil || c.msg.Namespace == *c.cond.Namespace
}

func (c checkable) relationMatches() bool {
	return c.cond.Relation == nil || c.msg.Relation == *c.cond.Relation
}

func (c checkable) tupleIDMatches() bool {
	return c.cond.TupleID == nil || c.msg.TupleID == *c.cond.TupleID
}

func (c checkable) prevTupleIDMatches() bool {
	return c.cond.PrevTupleID == nil || c.msg.PrevTupleID == *c.cond.PrevTupleID
}

func (c checkable) fieldsMatch() bool {
	msgFieldsContains := makeFieldsIndex(c.msg.Fields)

	for n, v := range c.cond.FieldsMatch {
		if !msgFieldsContains(n, v) {
			return false
		}
	}

	return true
}

func makeFieldsIndex(fields []message.Field) func(string, interface{}) bool {
	index := make(map[string]interface{})

	for _, f := range fields {
		index[fmt.Sprintf("%v=%v", f.Name, f.Value)] = true
	}

	return func(name string, value interface{}) bool {
		if _, ok := index[fmt.Sprintf("%v=%v", name, value)]; ok {
			return true
		}

		return false
	}
}
<commit_msg>make condition names match field names for consistency<commit_after>package tko

import (
	"encoding/json"
	"fmt"

	"github.com/MediaMath/keryxlib/message"
)

//ConditionDefinition wraps around the condition and allows it to be parsed generically.  Only 1 of the fields will be returned if multiple exist
type ConditionDefinition struct {
	IsTransaction *TransactionIDMatches  `json:"transaction_is"`
	HasMessage    *HasMessage            `json:"has_message"`
	Not           *ConditionDefinition   `json:"not"`
	AnyOf         *[]ConditionDefinition `json:"any_of"`
	AllOf         *[]ConditionDefinition `json:"all_of"`
}

func ReadConditionFromJSON(jsonStr string) (Condition, error) {
	var parsed ConditionDefinition
	err := json.Unmarshal([]byte(jsonStr), &parsed)

	if err != nil {
		return nil, err
	}

	return conditionFromDefinition(parsed)
}

func conditionFromDefinition(parsed ConditionDefinition) (Condition, error) {
	if parsed.IsTransaction != nil {
		return parsed.IsTransaction, nil
	}

	if parsed.HasMessage != nil {
		return parsed.HasMessage, nil
	}

	if parsed.Not != nil {
		cond, err := conditionFromDefinition(*parsed.Not)
		if err != nil {
			return nil, err
		}
		return &Not{cond}, nil
	}

	if parsed.AnyOf != nil {
		conditions := []Condition{}
		for _, definition := range *parsed.AnyOf {
			cond, err := conditionFromDefinition(definition)
			if err != nil {
				return nil, err
			}
			conditions = append(conditions, cond)
		}

		return AnyOf(conditions), nil
	}

	if parsed.AllOf != nil {
		conditions := []Condition{}
		for _, definition := range *parsed.AllOf {
			cond, err := conditionFromDefinition(definition)
			if err != nil {
				return nil, err
			}
			conditions = append(conditions, cond)
		}

		return AllOf(conditions), nil
	}

	return nil, fmt.Errorf("Unknown condition: %v", parsed)
}

//Not will return the inverse of the underlying condition.
type Not struct {
	condition Condition
}

func (c *Not) Check(txn *message.Transaction) bool {
	orig := c.condition.Check(txn)
	return !orig
}

//AnyOf will return true if any of its underlying Conditions return true. Logical Or.
type AnyOf []Condition

func (c AnyOf) Check(txn *message.Transaction) bool {
	if len(c) == 0 {
		return false
	}

	for _, condition := range c {
		if condition.Check(txn) {
			return true
		}
	}

	return false
}

//AllOf will return true if all of its underlying Conditions return true. Logical And.
type AllOf []Condition

func (c AllOf) Check(txn *message.Transaction) bool {
	if len(c) == 0 {
		return true
	}

	for _, condition := range c {
		if !condition.Check(txn) {
			return false
		}
	}

	return true
}

//TransactionIDMatches will match a specific TransactionID
type TransactionIDMatches struct {
	TransactionID uint32 `json:"xid"`
}

func (c *TransactionIDMatches) Check(txn *message.Transaction) bool {
	return c.TransactionID == txn.TransactionID
}

//HasMessage will match if any message in the transaction matches the provided message.
type HasMessage struct {
	Type          *message.Type          `json:"type"`
	DatabaseName  *string                `json:"db"`
	Namespace     *string                `json:"ns`
	Relation      *string                `json:"rel"`
	TupleID       *string                `json:"ctid"`
	PrevTupleID   *string                `json:"prev_ctid"`
	FieldsMatch   map[string]interface{} `json:"fields_match"`
	MissingFields *bool                  `json:"missing_fields"`
}

func (c *HasMessage) Check(txn *message.Transaction) bool {
	for _, msg := range txn.Messages {
		if c.checkMessage(msg) {
			return true
		}
	}

	return false
}

func (c *HasMessage) checkMessage(m message.Message) bool {
	chk := checkable{c, m}
	return chk.typeMatches() &&
		chk.databaseNameMatches() &&
		chk.namespaceMatches() &&
		chk.relationMatches() &&
		chk.tupleIDMatches() &&
		chk.prevTupleIDMatches() &&
		chk.missingFields() &&
		chk.fieldsMatch()
}

type checkable struct {
	cond *HasMessage
	msg  message.Message
}

func (c checkable) missingFields() bool {
	return c.cond.MissingFields == nil || (c.msg.Type != message.DeleteMessage && (len(c.msg.Fields) == 0) == *c.cond.MissingFields)
}

func (c checkable) typeMatches() bool {
	return c.cond.Type == nil || c.msg.Type == *c.cond.Type
}

func (c checkable) databaseNameMatches() bool {
	return c.cond.DatabaseName == nil || c.msg.DatabaseName == *c.cond.DatabaseName
}

func (c checkable) namespaceMatches() bool {
	return c.cond.Namespace == nil || c.msg.Namespace == *c.cond.Namespace
}

func (c checkable) relationMatches() bool {
	return c.cond.Relation == nil || c.msg.Relation == *c.cond.Relation
}

func (c checkable) tupleIDMatches() bool {
	return c.cond.TupleID == nil || c.msg.TupleID == *c.cond.TupleID
}

func (c checkable) prevTupleIDMatches() bool {
	return c.cond.PrevTupleID == nil || c.msg.PrevTupleID == *c.cond.PrevTupleID
}

func (c checkable) fieldsMatch() bool {
	msgFieldsContains := makeFieldsIndex(c.msg.Fields)

	for n, v := range c.cond.FieldsMatch {
		if !msgFieldsContains(n, v) {
			return false
		}
	}

	return true
}

func makeFieldsIndex(fields []message.Field) func(string, interface{}) bool {
	index := make(map[string]interface{})

	for _, f := range fields {
		index[fmt.Sprintf("%v=%v", f.Name, f.Value)] = true
	}

	return func(name string, value interface{}) bool {
		if _, ok := index[fmt.Sprintf("%v=%v", name, value)]; ok {
			return true
		}

		return false
	}
}
<|endoftext|>


<commit_before>package tool

import (
	"labix.org/v2/mgo/bson"
	"reflect"
	"testing"
)

var fb = &Tool{bson.NewObjectId(), "findbugs", JAVA, "/home/disco/apps/findbugs-2.0.2/lib/findbugs.jar", "warning_count", WARNS, []string{JAVA, "-jar"}, []string{"-textui", "-low"}, bson.M{}, PKG_PATH}
var javac = &Tool{bson.NewObjectId(), COMPILE, JAVA, JAVAC, WARNS, ERRS, []string{}, []string{"-implicit:class"}, bson.M{CP: ""}, FILE_PATH}

func TestGetArgs(t *testing.T) {
	fbExp := []string{"java", "-jar", "/home/disco/apps/findbugs-2.0.2/lib/findbugs.jar", "-textui", "-low", "here"}
	res := fb.GetArgs("here")
	if !reflect.DeepEqual(fbExp, res) {
		t.Error("Arguments not computed correctly", res)
	}
	compExp := []string{JAVAC, "-implicit:class", CP, "there", "here"}
	res = javac.GetArgs("here")
	if reflect.DeepEqual(compExp, res) {
		t.Error("Arguments not computed correctly", res)
	}
	javac.setFlagArgs(map[string]string{CP: "there"})
	res = javac.GetArgs("here")
	if !reflect.DeepEqual(compExp, res) {
		t.Error("Arguments not computed correctly", res)
	}

}
<commit_msg>added tool tests<commit_after>package tool

import (
	"labix.org/v2/mgo/bson"
	"reflect"
	"testing"
	"os"
	"github.com/godfried/cabanga/util"
	"path/filepath"
)

func TestGetArgs(t *testing.T) {
	fb := &Tool{bson.NewObjectId(), "findbugs", JAVA, "/home/disco/apps/findbugs-2.0.2/lib/findbugs.jar", "warning_count", WARNS, []string{JAVA, "-jar"}, []string{"-textui", "-low"}, bson.M{}, PKG_PATH}
	javac := &Tool{bson.NewObjectId(), COMPILE, JAVA, JAVAC, WARNS, ERRS, []string{}, []string{"-implicit:class"}, bson.M{CP: ""}, FILE_PATH}
	fbExp := []string{"java", "-jar", "/home/disco/apps/findbugs-2.0.2/lib/findbugs.jar", "-textui", "-low", "here"}
	res := fb.GetArgs("here")
	if !reflect.DeepEqual(fbExp, res) {
		t.Error("Arguments not computed correctly", res)
	}
	compExp := []string{JAVAC, "-implicit:class", CP, "there", "here"}
	res = javac.GetArgs("here")
	if reflect.DeepEqual(compExp, res) {
		t.Error("Arguments not computed correctly", res)
	}
	javac.setFlagArgs(map[string]string{CP: "there"})
	res = javac.GetArgs("here")
	if !reflect.DeepEqual(compExp, res) {
		t.Error("Arguments not computed correctly", res)
	}

}

func TestSetFlagArgs(t *testing.T){
	javac := &Tool{bson.NewObjectId(), COMPILE, JAVA, JAVAC, WARNS, ERRS, []string{}, []string{"-implicit:class"}, bson.M{CP: ""}, FILE_PATH}
	expected := bson.M{CP:"there"}
	javac.setFlagArgs(map[string]string{CP:"there"})
	if !reflect.DeepEqual(expected, javac.ArgFlags){
		t.Error("Flags not set properly", expected, javac.ArgFlags)
	}
}

func TestRunCommand(t *testing.T){
	failCmd := []string{"chmod", "777"}
	_, _, ok, err := RunCommand(failCmd...)
	if !ok || err == nil{
		t.Error("Command should have failed", err)
	}
	succeedCmd := []string{"ls","-a","-l"}
	_, _, ok, err = RunCommand(succeedCmd...)
	if !ok || err != nil{
		t.Error(err)
	}
	noCmd := []string{"lsa"}
	_, _, ok, err = RunCommand(noCmd...)
	if ok{
		t.Error("Command should not have started", err)
	}
}

func TestRunTool(t *testing.T){
	fileId := bson.NewObjectId()
	javac := &Tool{bson.NewObjectId(), COMPILE, JAVA, JAVAC, WARNS, ERRS, []string{}, []string{"-implicit:class"}, bson.M{CP: ""}, FILE_PATH}
	ti, err := setupTarget()
	if err != nil{
		t.Error(err)
	}
	_, err = RunTool(fileId, ti, javac, map[string]string{CP:ti.Dir})
	if err != nil{
		t.Error(err)
	}
	os.RemoveAll(ti.Dir)
}

func setupTarget() (*TargetInfo, error){
	var fileData = []byte(`package bermuda;

public class Triangle {
        public int maxpath(int[][] tri) {
                int h = tri.length;
                for (int j = h - 2; j >= 0; j--) {
                        for (int i = 0; i <= j; i++) {
                                tri[i][j] = Math.max(tri[i + 1][j], tri[i + 1][j + 1]);
                        }
                }
                return tri[0][0];
        }
}`)
	fname := "Triangle.java"
	pkg := "bermuda"

	dir := filepath.Join(os.TempDir(), "test")
	ti := NewTarget("Triangle", fname, "java", pkg, dir)
	err := util.SaveFile(filepath.Join(dir, ti.Package), ti.FullName(), fileData)
	if err != nil {
		return nil, err
	}
	return ti, nil
}

<|endoftext|>


<commit_before>/*
Copyright 2021 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"net"

	"github.com/gravitational/trace"

	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/srv/alpnproxy"
	alpncommon "github.com/gravitational/teleport/lib/srv/alpnproxy/common"
	"github.com/gravitational/teleport/lib/utils"
)

func onProxyCommandSSH(cf *CLIConf) error {
	client, err := makeClient(cf, false)
	if err != nil {
		return trace.Wrap(err)
	}

	address, err := utils.ParseAddr(client.WebProxyAddr)
	if err != nil {
		return trace.Wrap(err)
	}

	lp, err := alpnproxy.NewLocalProxy(alpnproxy.LocalProxyConfig{
		RemoteProxyAddr:    client.WebProxyAddr,
		Protocol:           alpncommon.ProtocolProxySSH,
		InsecureSkipVerify: cf.InsecureSkipVerify,
		ParentContext:      cf.Context,
		SNI:                address.Host(),
		SSHUser:            cf.Username,
		SSHUserHost:        cf.UserHost,
		SSHHostKeyCallback: client.HostKeyCallback,
		SSHTrustedCluster:  cf.SiteName,
	})
	if err != nil {
		return trace.Wrap(err)
	}
	defer lp.Close()
	if err := lp.SSHProxy(); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

func onProxyCommandDB(cf *CLIConf) error {
	client, err := makeClient(cf, false)
	if err != nil {
		return trace.Wrap(err)
	}
	database, err := pickActiveDatabase(cf)
	if err != nil {
		return trace.Wrap(err)
	}

	addr := "localhost:0"
	if cf.LocalProxyPort != "" {
		addr = fmt.Sprintf("127.0.0.1:%s", cf.LocalProxyPort)
	}
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return trace.Wrap(err)
	}
	defer func() {
		if err := listener.Close(); err != nil {
			log.WithError(err).Warnf("Failed to close listener.")
		}
	}()
	lp, err := mkLocalProxy(cf, client.WebProxyAddr, database.Protocol, listener)
	if err != nil {
		return trace.Wrap(err)
	}
	go func() {
		<-cf.Context.Done()
		lp.Close()
	}()

	fmt.Printf("Started DB proxy on %s\n", listener.Addr())

	defer lp.Close()
	if err := lp.Start(cf.Context); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

func mkLocalProxy(cf *CLIConf, remoteProxyAddr string, protocol string, listener net.Listener) (*alpnproxy.LocalProxy, error) {
	alpnProtocol, err := toALPNProtocol(protocol)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	address, err := utils.ParseAddr(remoteProxyAddr)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	lp, err := alpnproxy.NewLocalProxy(alpnproxy.LocalProxyConfig{
		InsecureSkipVerify: cf.InsecureSkipVerify,
		RemoteProxyAddr:    remoteProxyAddr,
		Protocol:           alpnProtocol,
		Listener:           listener,
		ParentContext:      cf.Context,
		SNI:                address.Host(),
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return lp, nil
}

func toALPNProtocol(dbProtocol string) (alpncommon.Protocol, error) {
	switch dbProtocol {
	case defaults.ProtocolMySQL:
		return alpncommon.ProtocolMySQL, nil
	case defaults.ProtocolPostgres, defaults.ProtocolCockroachDB:
		return alpncommon.ProtocolPostgres, nil
	case defaults.ProtocolMongoDB:
		return alpncommon.ProtocolMongoDB, nil
	default:
		return "", trace.NotImplemented("%q protocol is not supported", dbProtocol)
	}
}
<commit_msg>Align the user message printed during the 'tsh proxy db' command (#8681)<commit_after>/*
Copyright 2021 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"net"
	"os"
	"text/template"

	"github.com/gravitational/trace"

	libclient "github.com/gravitational/teleport/lib/client"
	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/srv/alpnproxy"
	alpncommon "github.com/gravitational/teleport/lib/srv/alpnproxy/common"
	"github.com/gravitational/teleport/lib/utils"
)

func onProxyCommandSSH(cf *CLIConf) error {
	client, err := makeClient(cf, false)
	if err != nil {
		return trace.Wrap(err)
	}

	address, err := utils.ParseAddr(client.WebProxyAddr)
	if err != nil {
		return trace.Wrap(err)
	}

	lp, err := alpnproxy.NewLocalProxy(alpnproxy.LocalProxyConfig{
		RemoteProxyAddr:    client.WebProxyAddr,
		Protocol:           alpncommon.ProtocolProxySSH,
		InsecureSkipVerify: cf.InsecureSkipVerify,
		ParentContext:      cf.Context,
		SNI:                address.Host(),
		SSHUser:            cf.Username,
		SSHUserHost:        cf.UserHost,
		SSHHostKeyCallback: client.HostKeyCallback,
		SSHTrustedCluster:  cf.SiteName,
	})
	if err != nil {
		return trace.Wrap(err)
	}
	defer lp.Close()
	if err := lp.SSHProxy(); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

func onProxyCommandDB(cf *CLIConf) error {
	client, err := makeClient(cf, false)
	if err != nil {
		return trace.Wrap(err)
	}
	database, err := pickActiveDatabase(cf)
	if err != nil {
		return trace.Wrap(err)
	}

	addr := "localhost:0"
	if cf.LocalProxyPort != "" {
		addr = fmt.Sprintf("127.0.0.1:%s", cf.LocalProxyPort)
	}
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return trace.Wrap(err)
	}
	defer func() {
		if err := listener.Close(); err != nil {
			log.WithError(err).Warnf("Failed to close listener.")
		}
	}()
	lp, err := mkLocalProxy(cf, client.WebProxyAddr, database.Protocol, listener)
	if err != nil {
		return trace.Wrap(err)
	}
	go func() {
		<-cf.Context.Done()
		lp.Close()
	}()

	profile, err := libclient.StatusCurrent("", cf.Proxy)
	if err != nil {
		return trace.Wrap(err)
	}

	err = dbProxyTpl.Execute(os.Stdout, map[string]string{
		"database": database.ServiceName,
		"address":  listener.Addr().String(),
		"ca":       profile.CACertPath(),
		"cert":     profile.DatabaseCertPath(database.ServiceName),
		"key":      profile.KeyPath(),
	})
	if err != nil {
		return trace.Wrap(err)
	}

	defer lp.Close()
	if err := lp.Start(cf.Context); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

func mkLocalProxy(cf *CLIConf, remoteProxyAddr string, protocol string, listener net.Listener) (*alpnproxy.LocalProxy, error) {
	alpnProtocol, err := toALPNProtocol(protocol)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	address, err := utils.ParseAddr(remoteProxyAddr)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	lp, err := alpnproxy.NewLocalProxy(alpnproxy.LocalProxyConfig{
		InsecureSkipVerify: cf.InsecureSkipVerify,
		RemoteProxyAddr:    remoteProxyAddr,
		Protocol:           alpnProtocol,
		Listener:           listener,
		ParentContext:      cf.Context,
		SNI:                address.Host(),
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return lp, nil
}

func toALPNProtocol(dbProtocol string) (alpncommon.Protocol, error) {
	switch dbProtocol {
	case defaults.ProtocolMySQL:
		return alpncommon.ProtocolMySQL, nil
	case defaults.ProtocolPostgres, defaults.ProtocolCockroachDB:
		return alpncommon.ProtocolPostgres, nil
	case defaults.ProtocolMongoDB:
		return alpncommon.ProtocolMongoDB, nil
	default:
		return "", trace.NotImplemented("%q protocol is not supported", dbProtocol)
	}
}

// dbProxyTpl is the message that gets printed to a user when a database proxy is started.
var dbProxyTpl = template.Must(template.New("").Parse(`Started DB proxy on {{.address}}

Use following credentials to connect to the {{.database}} proxy:
  ca_file={{.ca}}
  cert_file={{.cert}}
  key_file={{.key}}
`))
<|endoftext|>


<commit_before>package cli

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise-cli/bitrise"
	"github.com/bitrise-io/bitrise-cli/models"
	"github.com/codegangsta/cli"
)

func getWorkFlowPathInCurrentFolder() (string, error) {
	fileInfos, err := ioutil.ReadDir("./")
	if err != nil {
		return "", err
	}

	matches := 0
	workFlowName := ""
	for _, fileInfo := range fileInfos {
		match, e := regexp.MatchString("([a-z]+).json", fileInfo.Name())
		if e != nil {
			return "", err
		}
		if match {
			matches = matches + 1
			workFlowName = fileInfo.Name()
		}
	}

	if matches == 0 {
		return "", errors.New("No workflow json found")
	}
	if matches > 1 {
		return "", errors.New("More then one possible workflow json found")
	}

	return workFlowName, nil
}

func activateAndRunSteps(workFlow models.WorkFlowModel) error {
	for _, step := range workFlow.Steps {
		stepDir := "./steps/" + step.Id + "/" + step.VersionTag + "/"

		err := bitrise.RunStepmanActivate(step.Id, step.VersionTag, stepDir)
		if err != nil {
			log.Errorln("Failed to run stepman activate")
			return err
		}

		log.Infof("Step activated: %s (%s)", step.Id, step.VersionTag)

		runStep(step)
	}
	return nil
}

func runStep(step models.StepModel) error {
	// Add step envs
	for _, input := range step.Inputs {
		if input.Value != nil {
			err := bitrise.RunEnvmanAdd(*input.MappedTo, *input.Value)

			if step.Id == "hipchat" && *input.MappedTo == "HIPCHAT_MESSAGE" {
				log.Info("!!!!!!!!!!!Message:", *input.Value)
			}

			if err != nil {
				log.Errorln("Failed to run envman add")
				return err
			}
		}
	}

	stepDir := "./steps/" + step.Id + "/" + step.VersionTag + "/"
	cmd := fmt.Sprintf("bash %sstep.sh", stepDir)
	err := bitrise.RunEnvmanRun(cmd)
	if err != nil {
		log.Errorln("Failed to run envman run")
		return err
	}

	log.Infof("Step executed: %s (%s)", step.Id, step.VersionTag)
	return nil
}

func doRun(c *cli.Context) {
	log.Info("Run")

	// Input validation
	workFlowJsonPath := c.String(PATH_KEY)
	if workFlowJsonPath == "" {
		log.Infoln("Workflow json path not defined, try search in current folder")

		workFlowName, err := getWorkFlowPathInCurrentFolder()
		if err != nil {
			log.Errorln("Failed to find workflow json:", err)
			return
		}
		workFlowJsonPath = "./" + workFlowName
	}

	os.Setenv("ENVMAN_ENVSTORE_PATH", "/Users/godrei/develop/bitrise/bitrise-cli-test/envstore.yml")
	os.Setenv("BITRISE_STEP_FORMATTED_OUTPUT_FILE_PATH", "/Users/godrei/develop/bitrise/bitrise-cli-test/formout.md")
	err := bitrise.RunEnvmanInit()
	if err != nil {
		log.Error("Failed to run envman init")
		return
	}

	// Run work flow
	workFlow, err := bitrise.ReadWorkFlowJson(workFlowJsonPath)
	if err != nil {
		log.Errorln("Failed to read work flow:", err)
		return
	}

	err = activateAndRunSteps(workFlow)
	if err != nil {
		log.Errorln("Failed to activate steps:", err)
		return
	}
}
<commit_msg>removed code junk<commit_after>package cli

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise-cli/bitrise"
	"github.com/bitrise-io/bitrise-cli/models"
	"github.com/codegangsta/cli"
)

func getWorkFlowPathInCurrentFolder() (string, error) {
	fileInfos, err := ioutil.ReadDir("./")
	if err != nil {
		return "", err
	}

	matches := 0
	workFlowName := ""
	for _, fileInfo := range fileInfos {
		match, e := regexp.MatchString("([a-z]+).json", fileInfo.Name())
		if e != nil {
			return "", err
		}
		if match {
			matches = matches + 1
			workFlowName = fileInfo.Name()
		}
	}

	if matches == 0 {
		return "", errors.New("No workflow json found")
	}
	if matches > 1 {
		return "", errors.New("More then one possible workflow json found")
	}

	return workFlowName, nil
}

func activateAndRunSteps(workFlow models.WorkFlowModel) error {
	for _, step := range workFlow.Steps {
		stepDir := "./steps/" + step.Id + "/" + step.VersionTag + "/"

		err := bitrise.RunStepmanActivate(step.Id, step.VersionTag, stepDir)
		if err != nil {
			log.Errorln("Failed to run stepman activate")
			return err
		}

		log.Infof("Step activated: %s (%s)", step.Id, step.VersionTag)

		runStep(step)
	}
	return nil
}

func runStep(step models.StepModel) error {
	// Add step envs
	for _, input := range step.Inputs {
		if input.Value != nil {
			err := bitrise.RunPipedEnvmanAdd(*input.MappedTo, *input.Value)
			if err != nil {
				log.Errorln("Failed to run envman add")
				return err
			}
		}
	}

	stepDir := "./steps/" + step.Id + "/" + step.VersionTag + "/"
	cmd := fmt.Sprintf("bash %sstep.sh", stepDir)
	err := bitrise.RunEnvmanRun(cmd)
	if err != nil {
		log.Errorln("Failed to run envman run")
		return err
	}

	log.Infof("Step executed: %s (%s)", step.Id, step.VersionTag)
	return nil
}

func doRun(c *cli.Context) {
	log.Info("Run")

	// Input validation
	workFlowJsonPath := c.String(PATH_KEY)
	if workFlowJsonPath == "" {
		log.Infoln("Workflow json path not defined, try search in current folder")

		workFlowName, err := getWorkFlowPathInCurrentFolder()
		if err != nil {
			log.Errorln("Failed to find workflow json:", err)
			return
		}
		workFlowJsonPath = "./" + workFlowName
	}

	os.Setenv("ENVMAN_ENVSTORE_PATH", "/Users/godrei/develop/bitrise/bitrise-cli-test/envstore.yml")
	os.Setenv("BITRISE_STEP_FORMATTED_OUTPUT_FILE_PATH", "/Users/godrei/develop/bitrise/bitrise-cli-test/formout.md")
	err := bitrise.RunEnvmanInit()
	if err != nil {
		log.Error("Failed to run envman init")
		return
	}

	// Run work flow
	workFlow, err := bitrise.ReadWorkFlowJson(workFlowJsonPath)
	if err != nil {
		log.Errorln("Failed to read work flow:", err)
		return
	}

	err = activateAndRunSteps(workFlow)
	if err != nil {
		log.Errorln("Failed to activate steps:", err)
		return
	}
}
<|endoftext|>


<commit_before>package runtime

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"

	"github.com/docker/containerd/specs"
	"github.com/opencontainers/runc/libcontainer"
	ocs "github.com/opencontainers/specs/specs-go"
)

func getRootIDs(s *specs.Spec) (int, int, error) {
	if s == nil {
		return 0, 0, nil
	}
	var hasUserns bool
	for _, ns := range s.Linux.Namespaces {
		if ns.Type == ocs.UserNamespace {
			hasUserns = true
			break
		}
	}
	if !hasUserns {
		return 0, 0, nil
	}
	uid := hostIDFromMap(0, s.Linux.UIDMappings)
	gid := hostIDFromMap(0, s.Linux.GIDMappings)
	return uid, gid, nil
}

func (c *container) State() State {
	proc := c.processes["init"]
	if proc == nil {
		return Stopped
	}
	return proc.State()
}

func (c *container) Runtime() string {
	return c.runtime
}

func (c *container) Pause() error {
	return exec.Command(c.runtime, "pause", c.id).Run()
}

func (c *container) Resume() error {
	return exec.Command(c.runtime, "resume", c.id).Run()
}

func (c *container) Checkpoints() ([]Checkpoint, error) {
	dirs, err := ioutil.ReadDir(filepath.Join(c.bundle, "checkpoints"))
	if err != nil {
		return nil, err
	}
	var out []Checkpoint
	for _, d := range dirs {
		if !d.IsDir() {
			continue
		}
		path := filepath.Join(c.bundle, "checkpoints", d.Name(), "config.json")
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil, err
		}
		var cpt Checkpoint
		if err := json.Unmarshal(data, &cpt); err != nil {
			return nil, err
		}
		out = append(out, cpt)
	}
	return out, nil
}

func (c *container) Checkpoint(cpt Checkpoint) error {
	if err := os.MkdirAll(filepath.Join(c.bundle, "checkpoints"), 0755); err != nil {
		return err
	}
	path := filepath.Join(c.bundle, "checkpoints", cpt.Name)
	if err := os.Mkdir(path, 0755); err != nil {
		return err
	}
	f, err := os.Create(filepath.Join(path, "config.json"))
	if err != nil {
		return err
	}
	cpt.Created = time.Now()
	err = json.NewEncoder(f).Encode(cpt)
	f.Close()
	if err != nil {
		return err
	}
	args := []string{
		"checkpoint",
		"--image-path", path,
	}
	add := func(flags ...string) {
		args = append(args, flags...)
	}
	if !cpt.Exit {
		add("--leave-running")
	}
	if cpt.Shell {
		add("--shell-job")
	}
	if cpt.Tcp {
		add("--tcp-established")
	}
	if cpt.UnixSockets {
		add("--ext-unix-sk")
	}
	add(c.id)
	return exec.Command("runc", args...).Run()
}

func (c *container) DeleteCheckpoint(name string) error {
	return os.RemoveAll(filepath.Join(c.bundle, "checkpoints", name))
}

func (c *container) Start(checkpoint string, s Stdio) (Process, error) {
	processRoot := filepath.Join(c.root, c.id, InitProcessID)
	if err := os.Mkdir(processRoot, 0755); err != nil {
		return nil, err
	}
	cmd := exec.Command("containerd-shim",
		c.id, c.bundle, c.runtime,
	)
	cmd.Dir = processRoot
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}
	spec, err := c.readSpec()
	if err != nil {
		return nil, err
	}
	config := &processConfig{
		checkpoint:  checkpoint,
		root:        processRoot,
		id:          InitProcessID,
		c:           c,
		stdio:       s,
		spec:        spec,
		processSpec: specs.ProcessSpec(spec.Process),
	}
	p, err := newProcess(config)
	if err != nil {
		return nil, err
	}
	if err := c.startCmd(InitProcessID, cmd, p); err != nil {
		return nil, err
	}
	return p, nil
}

func (c *container) Exec(pid string, pspec specs.ProcessSpec, s Stdio) (pp Process, err error) {
	processRoot := filepath.Join(c.root, c.id, pid)
	if err := os.Mkdir(processRoot, 0755); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			c.RemoveProcess(pid)
		}
	}()
	cmd := exec.Command("containerd-shim",
		c.id, c.bundle, c.runtime,
	)
	cmd.Dir = processRoot
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}
	spec, err := c.readSpec()
	if err != nil {
		return nil, err
	}
	config := &processConfig{
		exec:        true,
		id:          pid,
		root:        processRoot,
		c:           c,
		processSpec: pspec,
		spec:        spec,
		stdio:       s,
	}
	p, err := newProcess(config)
	if err != nil {
		return nil, err
	}
	if err := c.startCmd(pid, cmd, p); err != nil {
		return nil, err
	}
	return p, nil
}

func (c *container) startCmd(pid string, cmd *exec.Cmd, p *process) error {
	if err := cmd.Start(); err != nil {
		if exErr, ok := err.(*exec.Error); ok {
			if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {
				return fmt.Errorf("containerd-shim not installed on system")
			}
		}
		return err
	}
	if err := waitForStart(p, cmd); err != nil {
		return err
	}
	c.processes[pid] = p
	return nil
}

func (c *container) getLibctContainer() (libcontainer.Container, error) {
	f, err := libcontainer.New("/run/runc", libcontainer.Cgroupfs)
	if err != nil {
		return nil, err
	}
	return f.Load(c.id)
}

func hostIDFromMap(id uint32, mp []ocs.IDMapping) int {
	for _, m := range mp {
		if (id >= m.ContainerID) && (id <= (m.ContainerID + m.Size - 1)) {
			return int(m.HostID + (id - m.ContainerID))
		}
	}
	return 0
}

func (c *container) Pids() ([]int, error) {
	container, err := c.getLibctContainer()
	if err != nil {
		return nil, err
	}
	return container.Processes()
}

func (c *container) Stats() (*Stat, error) {
	container, err := c.getLibctContainer()
	if err != nil {
		return nil, err
	}
	now := time.Now()
	stats, err := container.Stats()
	if err != nil {
		return nil, err
	}
	return &Stat{
		Timestamp: now,
		Data:      stats,
	}, nil
}

func (c *container) OOM() (OOM, error) {
	container, err := c.getLibctContainer()
	if err != nil {
		if lerr, ok := err.(libcontainer.Error); ok {
			// with oom registration sometimes the container can run, exit, and be destroyed
			// faster than we can get the state back so we can just ignore this
			if lerr.Code() == libcontainer.ContainerNotExists {
				return nil, ErrContainerExited
			}
		}
		return nil, err
	}
	state, err := container.State()
	if err != nil {
		return nil, err
	}
	memoryPath := state.CgroupPaths["memory"]
	return c.getMemeoryEventFD(memoryPath)
}

func (c *container) getMemeoryEventFD(root string) (*oom, error) {
	f, err := os.Open(filepath.Join(root, "memory.oom_control"))
	if err != nil {
		return nil, err
	}
	fd, _, serr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
	if serr != 0 {
		f.Close()
		return nil, serr
	}
	if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil {
		syscall.Close(int(fd))
		f.Close()
		return nil, err
	}
	return &oom{
		root:    root,
		id:      c.id,
		eventfd: int(fd),
		control: f,
	}, nil
}

func (c *container) writeEventFD(root string, cfd, efd int) error {
	f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd))
	return err
}

func waitForStart(p *process, cmd *exec.Cmd) error {
	for i := 0; i < 300; i++ {
		if _, err := p.getPidFromFile(); err != nil {
			if os.IsNotExist(err) || err == errInvalidPidInt {
				alive, err := isAlive(cmd)
				if err != nil {
					return err
				}
				if !alive {
					// runc could have failed to run the container so lets get the error
					// out of the logs or the shim could have encountered an error
					messages, err := readLogMessages(filepath.Join(p.root, "shim-log.json"))
					if err != nil {
						return err
					}
					for _, m := range messages {
						if m.Level == "error" {
							return errors.New(m.Msg)
						}
					}
					// no errors reported back from shim, check for runc/runtime errors
					messages, err = readLogMessages(filepath.Join(p.root, "log.json"))
					if err != nil {
						if os.IsNotExist(err) {
							return ErrContainerNotStarted
						}
						return err
					}
					for _, m := range messages {
						if m.Level == "error" {
							return errors.New(m.Msg)
						}
					}
					return ErrContainerNotStarted
				}
				time.Sleep(50 * time.Millisecond)
				continue
			}
			return err
		}
		return nil
	}
	return errNoPidFile
}

// isAlive checks if the shim that launched the container is still alive
func isAlive(cmd *exec.Cmd) (bool, error) {
	if err := syscall.Kill(cmd.Process.Pid, 0); err != nil {
		if err == syscall.ESRCH {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

type oom struct {
	id      string
	root    string
	control *os.File
	eventfd int
}

func (o *oom) ContainerID() string {
	return o.id
}

func (o *oom) FD() int {
	return o.eventfd
}

func (o *oom) Flush() {
	buf := make([]byte, 8)
	syscall.Read(o.eventfd, buf)
}

func (o *oom) Removed() bool {
	_, err := os.Lstat(filepath.Join(o.root, "cgroup.event_control"))
	return os.IsNotExist(err)
}

func (o *oom) Close() error {
	err := syscall.Close(o.eventfd)
	if cerr := o.control.Close(); err == nil {
		err = cerr
	}
	return err
}

type message struct {
	Level string `json:"level"`
	Msg   string `json:"msg"`
}

func readLogMessages(path string) ([]message, error) {
	var out []message
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	dec := json.NewDecoder(f)
	for {
		var m message
		if err := dec.Decode(&m); err != nil {
			if err == io.EOF {
				return out, nil
			}
			return nil, err
		}
		out = append(out, m)
	}
	return out, nil
}
<commit_msg>Have containerd-shim filename depend on containerd filename<commit_after>package runtime

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"

	"github.com/docker/containerd/specs"
	"github.com/opencontainers/runc/libcontainer"
	ocs "github.com/opencontainers/specs/specs-go"
)

var shimBinary = os.Args[0] + "-shim"

func getRootIDs(s *specs.Spec) (int, int, error) {
	if s == nil {
		return 0, 0, nil
	}
	var hasUserns bool
	for _, ns := range s.Linux.Namespaces {
		if ns.Type == ocs.UserNamespace {
			hasUserns = true
			break
		}
	}
	if !hasUserns {
		return 0, 0, nil
	}
	uid := hostIDFromMap(0, s.Linux.UIDMappings)
	gid := hostIDFromMap(0, s.Linux.GIDMappings)
	return uid, gid, nil
}

func (c *container) State() State {
	proc := c.processes["init"]
	if proc == nil {
		return Stopped
	}
	return proc.State()
}

func (c *container) Runtime() string {
	return c.runtime
}

func (c *container) Pause() error {
	return exec.Command(c.runtime, "pause", c.id).Run()
}

func (c *container) Resume() error {
	return exec.Command(c.runtime, "resume", c.id).Run()
}

func (c *container) Checkpoints() ([]Checkpoint, error) {
	dirs, err := ioutil.ReadDir(filepath.Join(c.bundle, "checkpoints"))
	if err != nil {
		return nil, err
	}
	var out []Checkpoint
	for _, d := range dirs {
		if !d.IsDir() {
			continue
		}
		path := filepath.Join(c.bundle, "checkpoints", d.Name(), "config.json")
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil, err
		}
		var cpt Checkpoint
		if err := json.Unmarshal(data, &cpt); err != nil {
			return nil, err
		}
		out = append(out, cpt)
	}
	return out, nil
}

func (c *container) Checkpoint(cpt Checkpoint) error {
	if err := os.MkdirAll(filepath.Join(c.bundle, "checkpoints"), 0755); err != nil {
		return err
	}
	path := filepath.Join(c.bundle, "checkpoints", cpt.Name)
	if err := os.Mkdir(path, 0755); err != nil {
		return err
	}
	f, err := os.Create(filepath.Join(path, "config.json"))
	if err != nil {
		return err
	}
	cpt.Created = time.Now()
	err = json.NewEncoder(f).Encode(cpt)
	f.Close()
	if err != nil {
		return err
	}
	args := []string{
		"checkpoint",
		"--image-path", path,
	}
	add := func(flags ...string) {
		args = append(args, flags...)
	}
	if !cpt.Exit {
		add("--leave-running")
	}
	if cpt.Shell {
		add("--shell-job")
	}
	if cpt.Tcp {
		add("--tcp-established")
	}
	if cpt.UnixSockets {
		add("--ext-unix-sk")
	}
	add(c.id)
	return exec.Command("runc", args...).Run()
}

func (c *container) DeleteCheckpoint(name string) error {
	return os.RemoveAll(filepath.Join(c.bundle, "checkpoints", name))
}

func (c *container) Start(checkpoint string, s Stdio) (Process, error) {
	processRoot := filepath.Join(c.root, c.id, InitProcessID)
	if err := os.Mkdir(processRoot, 0755); err != nil {
		return nil, err
	}
	cmd := exec.Command(shimBinary,
		c.id, c.bundle, c.runtime,
	)
	cmd.Dir = processRoot
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}
	spec, err := c.readSpec()
	if err != nil {
		return nil, err
	}
	config := &processConfig{
		checkpoint:  checkpoint,
		root:        processRoot,
		id:          InitProcessID,
		c:           c,
		stdio:       s,
		spec:        spec,
		processSpec: specs.ProcessSpec(spec.Process),
	}
	p, err := newProcess(config)
	if err != nil {
		return nil, err
	}
	if err := c.startCmd(InitProcessID, cmd, p); err != nil {
		return nil, err
	}
	return p, nil
}

func (c *container) Exec(pid string, pspec specs.ProcessSpec, s Stdio) (pp Process, err error) {
	processRoot := filepath.Join(c.root, c.id, pid)
	if err := os.Mkdir(processRoot, 0755); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			c.RemoveProcess(pid)
		}
	}()
	cmd := exec.Command(shimBinary,
		c.id, c.bundle, c.runtime,
	)
	cmd.Dir = processRoot
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}
	spec, err := c.readSpec()
	if err != nil {
		return nil, err
	}
	config := &processConfig{
		exec:        true,
		id:          pid,
		root:        processRoot,
		c:           c,
		processSpec: pspec,
		spec:        spec,
		stdio:       s,
	}
	p, err := newProcess(config)
	if err != nil {
		return nil, err
	}
	if err := c.startCmd(pid, cmd, p); err != nil {
		return nil, err
	}
	return p, nil
}

func (c *container) startCmd(pid string, cmd *exec.Cmd, p *process) error {
	if err := cmd.Start(); err != nil {
		if exErr, ok := err.(*exec.Error); ok {
			if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {
				return fmt.Errorf("%s not installed on system", shimBinary)
			}
		}
		return err
	}
	if err := waitForStart(p, cmd); err != nil {
		return err
	}
	c.processes[pid] = p
	return nil
}

func (c *container) getLibctContainer() (libcontainer.Container, error) {
	f, err := libcontainer.New("/run/runc", libcontainer.Cgroupfs)
	if err != nil {
		return nil, err
	}
	return f.Load(c.id)
}

func hostIDFromMap(id uint32, mp []ocs.IDMapping) int {
	for _, m := range mp {
		if (id >= m.ContainerID) && (id <= (m.ContainerID + m.Size - 1)) {
			return int(m.HostID + (id - m.ContainerID))
		}
	}
	return 0
}

func (c *container) Pids() ([]int, error) {
	container, err := c.getLibctContainer()
	if err != nil {
		return nil, err
	}
	return container.Processes()
}

func (c *container) Stats() (*Stat, error) {
	container, err := c.getLibctContainer()
	if err != nil {
		return nil, err
	}
	now := time.Now()
	stats, err := container.Stats()
	if err != nil {
		return nil, err
	}
	return &Stat{
		Timestamp: now,
		Data:      stats,
	}, nil
}

func (c *container) OOM() (OOM, error) {
	container, err := c.getLibctContainer()
	if err != nil {
		if lerr, ok := err.(libcontainer.Error); ok {
			// with oom registration sometimes the container can run, exit, and be destroyed
			// faster than we can get the state back so we can just ignore this
			if lerr.Code() == libcontainer.ContainerNotExists {
				return nil, ErrContainerExited
			}
		}
		return nil, err
	}
	state, err := container.State()
	if err != nil {
		return nil, err
	}
	memoryPath := state.CgroupPaths["memory"]
	return c.getMemeoryEventFD(memoryPath)
}

func (c *container) getMemeoryEventFD(root string) (*oom, error) {
	f, err := os.Open(filepath.Join(root, "memory.oom_control"))
	if err != nil {
		return nil, err
	}
	fd, _, serr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
	if serr != 0 {
		f.Close()
		return nil, serr
	}
	if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil {
		syscall.Close(int(fd))
		f.Close()
		return nil, err
	}
	return &oom{
		root:    root,
		id:      c.id,
		eventfd: int(fd),
		control: f,
	}, nil
}

func (c *container) writeEventFD(root string, cfd, efd int) error {
	f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd))
	return err
}

func waitForStart(p *process, cmd *exec.Cmd) error {
	for i := 0; i < 300; i++ {
		if _, err := p.getPidFromFile(); err != nil {
			if os.IsNotExist(err) || err == errInvalidPidInt {
				alive, err := isAlive(cmd)
				if err != nil {
					return err
				}
				if !alive {
					// runc could have failed to run the container so lets get the error
					// out of the logs or the shim could have encountered an error
					messages, err := readLogMessages(filepath.Join(p.root, "shim-log.json"))
					if err != nil {
						return err
					}
					for _, m := range messages {
						if m.Level == "error" {
							return errors.New(m.Msg)
						}
					}
					// no errors reported back from shim, check for runc/runtime errors
					messages, err = readLogMessages(filepath.Join(p.root, "log.json"))
					if err != nil {
						if os.IsNotExist(err) {
							return ErrContainerNotStarted
						}
						return err
					}
					for _, m := range messages {
						if m.Level == "error" {
							return errors.New(m.Msg)
						}
					}
					return ErrContainerNotStarted
				}
				time.Sleep(50 * time.Millisecond)
				continue
			}
			return err
		}
		return nil
	}
	return errNoPidFile
}

// isAlive checks if the shim that launched the container is still alive
func isAlive(cmd *exec.Cmd) (bool, error) {
	if err := syscall.Kill(cmd.Process.Pid, 0); err != nil {
		if err == syscall.ESRCH {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

type oom struct {
	id      string
	root    string
	control *os.File
	eventfd int
}

func (o *oom) ContainerID() string {
	return o.id
}

func (o *oom) FD() int {
	return o.eventfd
}

func (o *oom) Flush() {
	buf := make([]byte, 8)
	syscall.Read(o.eventfd, buf)
}

func (o *oom) Removed() bool {
	_, err := os.Lstat(filepath.Join(o.root, "cgroup.event_control"))
	return os.IsNotExist(err)
}

func (o *oom) Close() error {
	err := syscall.Close(o.eventfd)
	if cerr := o.control.Close(); err == nil {
		err = cerr
	}
	return err
}

type message struct {
	Level string `json:"level"`
	Msg   string `json:"msg"`
}

func readLogMessages(path string) ([]message, error) {
	var out []message
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	dec := json.NewDecoder(f)
	for {
		var m message
		if err := dec.Decode(&m); err != nil {
			if err == io.EOF {
				return out, nil
			}
			return nil, err
		}
		out = append(out, m)
	}
	return out, nil
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"time"

	"github.com/miekg/dns"
)

var (
	url = flag.String("url",
		"http://www.internic.net/domain/root.zone",
		"URL of the IANA root zone file. If empty, read from stdin")
	whois = flag.String("whois",
		"whois.iana.org",
		"Address of the root whois server to query")
	v = flag.Bool("v", false, "verbose output (to stderr)")

	dnsClient *dns.Client
)

type ZoneWhois struct {
	zone  string
	whois string
	msg   string
}

func main() {
	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	flag.Parse()

	var input io.Reader = os.Stdin

	if *url != "" {
		fmt.Fprintf(os.Stderr, "Fetching %s\n", *url)
		res, err := http.Get(*url)
		if err != nil {
			return err
		}
		if res.StatusCode != http.StatusOK {
			return fmt.Errorf("Bad GET status for %s: %d", *url, res.Status)
		}
		input = res.Body
		defer res.Body.Close()
	}

	zoneMap := make(map[string]string)

	fmt.Fprintf(os.Stderr, "Parsing root.zone\n")
	for token := range dns.ParseZone(input, "", "") {
		if token.Error != nil {
			return token.Error
		}
		header := token.RR.Header()
		if header.Rrtype != dns.TypeNS {
			continue
		}
		domain := strings.TrimSuffix(strings.ToLower(header.Name), ".")
		if domain == "" {
			continue
		}
		zoneMap[domain] = domain
	}

	// Sort zones
	zones := make([]string, 0, len(zoneMap))
	for zone, _ := range zoneMap {
		zones = append(zones, zone)
	}
	sort.Strings(zones)

	// Get whois servers for each zone
	re := regexp.MustCompile("whois:\\s+([a-z0-9\\-\\.]+)")
	c := make(chan ZoneWhois, len(zones))

	fmt.Fprintf(os.Stderr, "Querying whois and DNS for %d zones\n", len(zones))

	// Create 1 goroutine for each zone
	for i, zone := range zones {
		go func(zone string, i int) {
			zw := ZoneWhois{zone, "", ""}
			defer func() { c <- zw }()

			time.Sleep(time.Duration(i*100) * time.Millisecond) // Try not to hammer IANA

			res, err := querySocket(*whois, zone)
			if err != nil {
				return
			}

			// Look for whois: string
			matches := re.FindStringSubmatch(res)
			if matches != nil {
				zw.whois = matches[1]
				zw.msg = fmt.Sprintf("whois -h %s %s\t\t%s", *whois, zw.zone, zw.whois)
				return
			}

			// Check whois-servers.net
			host := zone + ".whois-servers.net"
			cname, err := queryCNAME(host)
			if cname == "" || err != nil {
				return
			}

			zw.whois = cname
			zw.msg = fmt.Sprintf("dig %s CNAME\t\t%s", host, zw.whois)
		}(zone, i)
	}

	// Collect from goroutines
	for i := 0; i < len(zones); i++ {
		select {
		case zw := <-c:
			if zw.msg == "" {
				fmt.Fprintf(os.Stderr, "No match for %s\n", zw.zone)
			} else if *v && zw.msg != "" {
				fmt.Fprintf(os.Stderr, "%s\n", zw.msg)
			}
		}
	}

	return nil
}

func querySocket(addr, query string) (string, error) {
	if !strings.Contains(addr, ":") {
		addr = addr + ":43"
	}
	c, err := net.Dial("tcp", addr)
	if err != nil {
		return "", err
	}
	defer c.Close()
	if _, err = fmt.Fprint(c, query, "\r\n"); err != nil {
		return "", err
	}
	res, err := ioutil.ReadAll(c)
	if err != nil {
		return "", err
	}
	return string(res), nil
}

func queryCNAME(host string) (string, error) {
	m := new(dns.Msg)
	m.RecursionDesired = true
	fqdn := dns.Fqdn(host)
	m.SetQuestion(fqdn, dns.TypeCNAME)
	dnsClient = new(dns.Client)
	r, _, err := dnsClient.Exchange(m, "8.8.8.8:53")
	if err != nil {
		return "", err
	} else if r.Rcode == dns.RcodeSuccess && r.Answer != nil && len(r.Answer) >= 1 {
		return r.Answer[0].Header().Name, nil
	}
	return "", nil
}
<commit_msg>Print stats.<commit_after>// +build ignore

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"time"

	"github.com/miekg/dns"
)

var (
	url = flag.String("url",
		"http://www.internic.net/domain/root.zone",
		"URL of the IANA root zone file. If empty, read from stdin")
	whois = flag.String("whois",
		"whois.iana.org",
		"Address of the root whois server to query")
	v = flag.Bool("v", false, "verbose output (to stderr)")

	dnsClient *dns.Client
)

type ZoneWhois struct {
	zone   string
	server string
	msg    string
	viaDNS bool
}

func main() {
	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	flag.Parse()

	var input io.Reader = os.Stdin

	if *url != "" {
		fmt.Fprintf(os.Stderr, "Fetching %s\n", *url)
		res, err := http.Get(*url)
		if err != nil {
			return err
		}
		if res.StatusCode != http.StatusOK {
			return fmt.Errorf("Bad GET status for %s: %d", *url, res.Status)
		}
		input = res.Body
		defer res.Body.Close()
	}

	zoneMap := make(map[string]ZoneWhois)

	fmt.Fprintf(os.Stderr, "Parsing root.zone\n")
	for token := range dns.ParseZone(input, "", "") {
		if token.Error != nil {
			return token.Error
		}
		header := token.RR.Header()
		if header.Rrtype != dns.TypeNS {
			continue
		}
		domain := strings.TrimSuffix(strings.ToLower(header.Name), ".")
		if domain == "" {
			continue
		}
		zoneMap[domain] = ZoneWhois{}
	}

	// Sort zones
	zones := make([]string, 0, len(zoneMap))
	for zone, _ := range zoneMap {
		zones = append(zones, zone)
	}
	sort.Strings(zones)

	// Get whois servers for each zone
	re := regexp.MustCompile("whois:\\s+([a-z0-9\\-\\.]+)")
	c := make(chan ZoneWhois, len(zones))

	fmt.Fprintf(os.Stderr, "Querying whois and DNS for %d zones\n", len(zones))

	// Create 1 goroutine for each zone
	for i, zone := range zones {
		go func(zone string, i int) {
			zw := ZoneWhois{zone, "", "", false}
			defer func() { c <- zw }()

			time.Sleep(time.Duration(i*100) * time.Millisecond) // Try not to hammer IANA

			res, err := querySocket(*whois, zone)
			if err != nil {
				return
			}

			// Look for whois: string
			matches := re.FindStringSubmatch(res)
			if matches != nil {
				zw.server = matches[1]
				zw.msg = fmt.Sprintf("whois -h %s %s\t\t%s", *whois, zw.zone, zw.server)
				return
			}

			// Check whois-servers.net
			host := zone + ".whois-servers.net"
			zw.server, err = queryCNAME(host)
			if zw.server == "" || err != nil {
				return
			}

			zw.msg = fmt.Sprintf("dig %s CNAME\t\t%s", host, zw.server)
			zw.viaDNS = true
		}(zone, i)
	}

	numMissing, numDNS := 0, 0

	// Collect from goroutines
	for i := 0; i < len(zones); i++ {
		select {
		case zw := <-c:
			if zw.msg == "" {
				fmt.Fprintf(os.Stderr, "No match for %s\n", zw.zone)
			} else if *v && zw.msg != "" {
				fmt.Fprintf(os.Stderr, "%s\n", zw.msg)
			}

			zoneMap[zw.zone] = zw

			if zw.server == "" {
				numMissing++
			}
			if zw.viaDNS {
				numDNS++
			}
		}
	}

	// Print stats
	fmt.Fprintf(os.Stderr, "Zones with whois servers:    %d (%d via DNS)\n",
		len(zones)-numMissing, numDNS)
	fmt.Fprintf(os.Stderr, "Zones without whois servers: %d (%.0f%%)\n",
		numMissing, float32(numMissing)/float32(len(zones))*float32(100))
	fmt.Fprintf(os.Stderr, "Total number of zones:       %d\n",
		len(zones))

	return nil
}

func querySocket(addr, query string) (string, error) {
	if !strings.Contains(addr, ":") {
		addr = addr + ":43"
	}
	c, err := net.Dial("tcp", addr)
	if err != nil {
		return "", err
	}
	defer c.Close()
	if _, err = fmt.Fprint(c, query, "\r\n"); err != nil {
		return "", err
	}
	res, err := ioutil.ReadAll(c)
	if err != nil {
		return "", err
	}
	return string(res), nil
}

func queryCNAME(host string) (string, error) {
	m := new(dns.Msg)
	m.RecursionDesired = true
	fqdn := dns.Fqdn(host)
	m.SetQuestion(fqdn, dns.TypeCNAME)
	dnsClient = new(dns.Client)
	r, _, err := dnsClient.Exchange(m, "8.8.8.8:53")
	if err != nil {
		return "", err
	} else if r.Rcode == dns.RcodeSuccess && r.Answer != nil && len(r.Answer) >= 1 {
		return r.Answer[0].Header().Name, nil
	}
	return "", nil
}
<|endoftext|>


<commit_before>package uaparser

import (
	"regexp"
	"strconv"
	"strings"
)

// ID type.
type ID uint

type pattern struct {
	id             ID
	mustContain    []string
	mustNotContain []string
	version        *regexp.Regexp
}

const (
	// UNKNOWN is the unknown ID.
	UNKNOWN ID = 1
)

// Version converts a major,minor version pair into a single number using (major * 10000) + minor.
func Version(major, minor int) int {
	return (major * 10000) + minor
}

func find(patterns []pattern, userAgent string) (id ID, version int) {
	userAgent = strings.ToLower(userAgent)

patterns:
	for _, pattern := range patterns {
		for _, token := range pattern.mustContain {
			if !strings.Contains(userAgent, token) {
				continue patterns
			}
		}

		for _, token := range pattern.mustNotContain {
			if strings.Contains(userAgent, token) {
				continue patterns
			}
		}

		id = pattern.id

		if pattern.version == nil {
			return
		}

		vs := pattern.version.FindStringSubmatch(userAgent)
		major := 0
		minor := 0

		if len(vs) > 1 {
			if v, err := strconv.ParseInt(vs[1], 10, 32); err == nil {
				major = int(v)
			}
		}
		if len(vs) > 2 {
			if v, err := strconv.ParseInt(vs[2], 10, 32); err == nil {
				minor = int(v)
			}
		}

		version = Version(major, minor)

		return
	}

	// Unknown
	id = 1

	return
}
<commit_msg>Added TODO<commit_after>package uaparser

import (
	"regexp"
	"strconv"
	"strings"
)

// ID type.
type ID uint

type pattern struct {
	id             ID
	mustContain    []string
	mustNotContain []string
	version        *regexp.Regexp
}

const (
	// UNKNOWN is the unknown ID.
	UNKNOWN ID = 1
)

// Version converts a major,minor version pair into a single number using (major * 10000) + minor.
func Version(major, minor int) int {
	return (major * 10000) + minor
}

func find(patterns []pattern, userAgent string) (id ID, version int) {
	userAgent = strings.ToLower(userAgent)

patterns:
	for _, pattern := range patterns {
		for _, token := range pattern.mustContain {
			// TODO: We can speed this up by copying http://golang.org/src/strings/strings.go#L159
			// and precalculating the hashsep.
			if !strings.Contains(userAgent, token) {
				continue patterns
			}
		}

		for _, token := range pattern.mustNotContain {
			if strings.Contains(userAgent, token) {
				continue patterns
			}
		}

		id = pattern.id

		if pattern.version == nil {
			return
		}

		vs := pattern.version.FindStringSubmatch(userAgent)
		major := 0
		minor := 0

		if len(vs) > 1 {
			if v, err := strconv.ParseInt(vs[1], 10, 32); err == nil {
				major = int(v)
			}
		}
		if len(vs) > 2 {
			if v, err := strconv.ParseInt(vs[2], 10, 32); err == nil {
				minor = int(v)
			}
		}

		version = Version(major, minor)

		return
	}

	// Unknown
	id = 1

	return
}
<|endoftext|>