<commit_before>package main

// Katakana is a table mapping katakana to Mora.
var Katakana = map[string]Mora{
	"ア": Mora{"", "a"}, "イ": Mora{"", "i"}, "ウ": Mora{"", "u"}, "エ": Mora{"", "e"}, "オ": Mora{"", "o"},
	"カ": Mora{"k", "a"}, "キ": Mora{"k", "i"}, "ク": Mora{"k", "u"}, "ケ": Mora{"k", "e"}, "コ": Mora{"k", "o"},
	"サ": Mora{"s", "a"}, "シ": Mora{"sh", "i"}, "ス": Mora{"s", "u"}, "セ": Mora{"s", "e"}, "ソ": Mora{"s", "o"},
	"タ": Mora{"t", "a"}, "チ": Mora{"ch", "i"}, "ツ": Mora{"ts", "u"}, "テ": Mora{"t", "e"}, "ト": Mora{"t", "o"},
	"ナ": Mora{"n", "a"}, "ニ": Mora{"n", "i"}, "ヌ": Mora{"n", "u"}, "ネ": Mora{"n", "e"}, "ノ": Mora{"n", "o"},
	"ハ": Mora{"h", "a"}, "ヒ": Mora{"h", "i"}, "フ": Mora{"f", "u"}, "ヘ": Mora{"h", "e"}, "ホ": Mora{"h", "o"},
	"マ": Mora{"m", "a"}, "ミ": Mora{"m", "i"}, "ム": Mora{"m", "u"}, "メ": Mora{"m", "e"}, "モ": Mora{"m", "o"},
	"ヤ": Mora{"y", "a"}, "ユ": Mora{"y", "u"}, "ヨ": Mora{"y", "o"},
	"ラ": Mora{"r", "a"}, "リ": Mora{"r", "i"}, "ル": Mora{"r", "u"}, "レ": Mora{"r", "e"}, "ロ": Mora{"r", "o"},
	"ワ": Mora{"w", "a"}, "ヲ": Mora{"", "o"}, "ン": Mora{"*n", "*n"},
	"ガ": Mora{"g", "a"}, "ギ": Mora{"g", "i"}, "グ": Mora{"g", "u"}, "ゲ": Mora{"g", "e"}, "ゴ": Mora{"g", "o"},
	"ザ": Mora{"z", "a"}, "ジ": Mora{"j", "i"}, "ズ": Mora{"z", "u"}, "ゼ": Mora{"z", "e"}, "ゾ": Mora{"z", "o"},
	"ダ": Mora{"d", "a"}, "ヂ": Mora{"j", "i"}, "ヅ": Mora{"z", "u"}, "デ": Mora{"d", "e"}, "ド": Mora{"d", "o"},
	"バ": Mora{"b", "a"}, "ビ": Mora{"b", "i"}, "ブ": Mora{"b", "u"}, "ベ": Mora{"b", "e"}, "ボ": Mora{"b", "o"},
	"パ": Mora{"p", "a"}, "ピ": Mora{"p", "i"}, "プ": Mora{"p", "u"}, "ペ": Mora{"p", "e"}, "ポ": Mora{"p", "o"},
	"キャ": Mora{"ky", "a"}, "キュ": Mora{"ky", "u"}, "キョ": Mora{"ky", "o"},
	"シャ": Mora{"sh", "a"}, "シュ": Mora{"sh", "u"}, "ショ": Mora{"sh", "o"},
	"チャ": Mora{"ch", "a"}, "チュ": Mora{"ch", "u"}, "チョ": Mora{"ch", "o"},
	"ニャ": Mora{"ny", "a"}, "ニュ": Mora{"ny", "u"}, "ニョ": Mora{"ny", "o"},
	"ヒャ": Mora{"hy", "a"}, "ヒュ": Mora{"hy", "u"}, "ヒョ": Mora{"hy", "o"},
	"ミャ": Mora{"my", "a"}, "ミュ": Mora{"my", "u"}, "ミョ": Mora{"my", "o"},
	"リャ": Mora{"ry", "a"}, "リュ": Mora{"ry", "u"}, "リョ": Mora{"ry", "o"},
	"ギャ": Mora{"gy", "a"}, "ギュ": Mora{"gy", "u"}, "ギョ": Mora{"gy", "o"},
	"ジャ": Mora{"j", "a"}, "ジュ": Mora{"j", "u"}, "ジョ": Mora{"j", "o"},
	"ビャ": Mora{"by", "a"}, "ビュ": Mora{"by", "u"}, "ビョ": Mora{"by", "o"},
	"ピャ": Mora{"py", "a"}, "ピュ": Mora{"py", "u"}, "ピョ": Mora{"py", "o"},
	"ファ": Mora{"f", "a"}, "フィ": Mora{"f", "i"}, "フェ": Mora{"f", "e"}, "フォ": Mora{"f", "o"},
	"フュ": Mora{"fy", "u"},
	"ウィ": Mora{"w", "i"}, "ウェ": Mora{"w", "e"}, "ウォ": Mora{"w", "o"},
	"ヴァ": Mora{"v", "a"}, "ヴィ": Mora{"v", "i"}, "ヴェ": Mora{"v", "e"}, "ヴォ": Mora{"v", "o"},
	"ツァ": Mora{"ts", "a"}, "ツィ": Mora{"ts", "i"}, "ツェ": Mora{"ts", "e"}, "ツォ": Mora{"ts", "o"},
	"チェ": Mora{"ch", "e"}, "シェ": Mora{"sh", "e"}, "ジェ": Mora{"j", "e"},
	"ティ": Mora{"t", "i"}, "ディ": Mora{"d", "i"},
	"デュ": Mora{"d", "u"}, "トゥ": Mora{"t", "u"},
	"ッ": Mora{"*xtu", "*xtu"},
}

// Mora is a property of mora.
type Mora struct{ Consonant, Vowel string }

// NewMorae converts s to []Mora.
func NewMorae(s string) ([]Mora, bool) {
	runes := []rune(s + "*")
	ans := make([]Mora, 0, len(runes)-1)
	for i := 0; i < len(runes)-1; i++ {
		if m, ok := Katakana[string(runes[i:i+2])]; ok {
			ans = append(ans, m)
			i++
		} else if m, ok := Katakana[string(runes[i])]; ok {
			ans = append(ans, m)
		} else if runes[i] == 'ー' && len(ans) > 0 {
			m := Mora{Consonant: "", Vowel: ans[len(ans)-1].Vowel}
			ans = append(ans, m)
		} else {
			return nil, false
		}
	}
	if len(ans) == 0 {
		return nil, false
	}
	return ans, true
}

// MoraWeight is a part of MoraeWeight.
type MoraWeight struct{ Consonant, Vowel float64 }

// NewMoraeWeight returns new MoraWeight instance from mw.
func NewMoraeWeight(mw []MoraWeight) *MoraeWeight {
	sum := 0.0
	for _, m := range mw {
		sum += m.Consonant
		sum += m.Vowel
	}
	return &MoraeWeight{mw: mw, sum: sum}
}

// MoraeWeight defines a weight of rhyming.
type MoraeWeight struct {
	mw  []MoraWeight
	sum float64
}

// Similarity returns coolness of rhyming on m0 + m1.
func (mw *MoraeWeight) Similarity(m0, m1 []Mora) float64 {
	minLen := len(mw.mw)
	for _, l := range []int{len(m0), len(m1)} {
		if minLen > l {
			minLen = l
		}
	}
	if minLen == 0 {
		return 0.0
	}

	weight := mw.mw[len(mw.mw)-minLen:]
	myM0 := m0[len(m0)-minLen:]
	myM1 := m1[len(m1)-minLen:]

	var sum float64
	for i := 0; i < minLen; i++ {
		if myM0[i].Consonant == myM1[i].Consonant {
			sum += weight[i].Consonant
		}
		if myM0[i].Vowel == myM1[i].Vowel {
			sum += weight[i].Vowel
		}
	}
	return sum / mw.sum
}
<commit_msg>add MoraDummy<commit_after>package main

// Katakana is a table mapping katakana to Mora.
var Katakana = map[string]Mora{
	"ア": Mora{"", "a"}, "イ": Mora{"", "i"}, "ウ": Mora{"", "u"}, "エ": Mora{"", "e"}, "オ": Mora{"", "o"},
	"カ": Mora{"k", "a"}, "キ": Mora{"k", "i"}, "ク": Mora{"k", "u"}, "ケ": Mora{"k", "e"}, "コ": Mora{"k", "o"},
	"サ": Mora{"s", "a"}, "シ": Mora{"sh", "i"}, "ス": Mora{"s", "u"}, "セ": Mora{"s", "e"}, "ソ": Mora{"s", "o"},
	"タ": Mora{"t", "a"}, "チ": Mora{"ch", "i"}, "ツ": Mora{"ts", "u"}, "テ": Mora{"t", "e"}, "ト": Mora{"t", "o"},
	"ナ": Mora{"n", "a"}, "ニ": Mora{"n", "i"}, "ヌ": Mora{"n", "u"}, "ネ": Mora{"n", "e"}, "ノ": Mora{"n", "o"},
	"ハ": Mora{"h", "a"}, "ヒ": Mora{"h", "i"}, "フ": Mora{"f", "u"}, "ヘ": Mora{"h", "e"}, "ホ": Mora{"h", "o"},
	"マ": Mora{"m", "a"}, "ミ": Mora{"m", "i"}, "ム": Mora{"m", "u"}, "メ": Mora{"m", "e"}, "モ": Mora{"m", "o"},
	"ヤ": Mora{"y", "a"}, "ユ": Mora{"y", "u"}, "ヨ": Mora{"y", "o"},
	"ラ": Mora{"r", "a"}, "リ": Mora{"r", "i"}, "ル": Mora{"r", "u"}, "レ": Mora{"r", "e"}, "ロ": Mora{"r", "o"},
	"ワ": Mora{"w", "a"}, "ヲ": Mora{"", "o"}, "ン": Mora{"*n", "*n"},
	"ガ": Mora{"g", "a"}, "ギ": Mora{"g", "i"}, "グ": Mora{"g", "u"}, "ゲ": Mora{"g", "e"}, "ゴ": Mora{"g", "o"},
	"ザ": Mora{"z", "a"}, "ジ": Mora{"j", "i"}, "ズ": Mora{"z", "u"}, "ゼ": Mora{"z", "e"}, "ゾ": Mora{"z", "o"},
	"ダ": Mora{"d", "a"}, "ヂ": Mora{"j", "i"}, "ヅ": Mora{"z", "u"}, "デ": Mora{"d", "e"}, "ド": Mora{"d", "o"},
	"バ": Mora{"b", "a"}, "ビ": Mora{"b", "i"}, "ブ": Mora{"b", "u"}, "ベ": Mora{"b", "e"}, "ボ": Mora{"b", "o"},
	"パ": Mora{"p", "a"}, "ピ": Mora{"p", "i"}, "プ": Mora{"p", "u"}, "ペ": Mora{"p", "e"}, "ポ": Mora{"p", "o"},
	"キャ": Mora{"ky", "a"}, "キュ": Mora{"ky", "u"}, "キョ": Mora{"ky", "o"},
	"シャ": Mora{"sh", "a"}, "シュ": Mora{"sh", "u"}, "ショ": Mora{"sh", "o"},
	"チャ": Mora{"ch", "a"}, "チュ": Mora{"ch", "u"}, "チョ": Mora{"ch", "o"},
	"ニャ": Mora{"ny", "a"}, "ニュ": Mora{"ny", "u"}, "ニョ": Mora{"ny", "o"},
	"ヒャ": Mora{"hy", "a"}, "ヒュ": Mora{"hy", "u"}, "ヒョ": Mora{"hy", "o"},
	"ミャ": Mora{"my", "a"}, "ミュ": Mora{"my", "u"}, "ミョ": Mora{"my", "o"},
	"リャ": Mora{"ry", "a"}, "リュ": Mora{"ry", "u"}, "リョ": Mora{"ry", "o"},
	"ギャ": Mora{"gy", "a"}, "ギュ": Mora{"gy", "u"}, "ギョ": Mora{"gy", "o"},
	"ジャ": Mora{"j", "a"}, "ジュ": Mora{"j", "u"}, "ジョ": Mora{"j", "o"},
	"ビャ": Mora{"by", "a"}, "ビュ": Mora{"by", "u"}, "ビョ": Mora{"by", "o"},
	"ピャ": Mora{"py", "a"}, "ピュ": Mora{"py", "u"}, "ピョ": Mora{"py", "o"},
	"ファ": Mora{"f", "a"}, "フィ": Mora{"f", "i"}, "フェ": Mora{"f", "e"}, "フォ": Mora{"f", "o"},
	"フュ": Mora{"fy", "u"},
	"ウィ": Mora{"w", "i"}, "ウェ": Mora{"w", "e"}, "ウォ": Mora{"w", "o"},
	"ヴァ": Mora{"v", "a"}, "ヴィ": Mora{"v", "i"}, "ヴェ": Mora{"v", "e"}, "ヴォ": Mora{"v", "o"},
	"ツァ": Mora{"ts", "a"}, "ツィ": Mora{"ts", "i"}, "ツェ": Mora{"ts", "e"}, "ツォ": Mora{"ts", "o"},
	"チェ": Mora{"ch", "e"}, "シェ": Mora{"sh", "e"}, "ジェ": Mora{"j", "e"},
	"ティ": Mora{"t", "i"}, "ディ": Mora{"d", "i"},
	"デュ": Mora{"d", "u"}, "トゥ": Mora{"t", "u"},
	"ッ": Mora{"*xtu", "*xtu"},
}

// MoraDummy is a dummy mora.
var MoraDummy = Mora{"*", "*"}

// Mora is a property of mora.
type Mora struct{ Consonant, Vowel string }

// NewMorae converts s to []Mora.
func NewMorae(s string) ([]Mora, bool) {
	runes := []rune(s + "*")
	ans := make([]Mora, 0, len(runes)-1)
	for i := 0; i < len(runes)-1; i++ {
		if m, ok := Katakana[string(runes[i:i+2])]; ok {
			ans = append(ans, m)
			i++
		} else if m, ok := Katakana[string(runes[i])]; ok {
			ans = append(ans, m)
		} else if runes[i] == 'ー' && len(ans) > 0 {
			m := Mora{Consonant: "", Vowel: ans[len(ans)-1].Vowel}
			ans = append(ans, m)
		} else {
			return nil, false
		}
	}
	if len(ans) == 0 {
		return nil, false
	}
	return ans, true
}

// MoraWeight is a part of MoraeWeight.
type MoraWeight struct{ Consonant, Vowel float64 }

// NewMoraeWeight returns new MoraWeight instance from mw.
func NewMoraeWeight(mw []MoraWeight) *MoraeWeight {
	sum := 0.0
	for _, m := range mw {
		sum += m.Consonant
		sum += m.Vowel
	}
	return &MoraeWeight{mw: mw, sum: sum}
}

// MoraeWeight defines a weight of rhyming.
type MoraeWeight struct {
	mw  []MoraWeight
	sum float64
}

// Similarity returns coolness of rhyming on m0 + m1.
func (mw *MoraeWeight) Similarity(m0, m1 []Mora) float64 {
	minLen := len(mw.mw)
	for _, l := range []int{len(m0), len(m1)} {
		if minLen > l {
			minLen = l
		}
	}
	if minLen == 0 {
		return 0.0
	}

	weight := mw.mw[len(mw.mw)-minLen:]
	myM0 := m0[len(m0)-minLen:]
	myM1 := m1[len(m1)-minLen:]

	var sum float64
	for i := 0; i < minLen; i++ {
		if myM0[i] == MoraDummy || myM1[i] == MoraDummy {
			continue
		}
		if myM0[i].Consonant == myM1[i].Consonant {
			sum += weight[i].Consonant
		}
		if myM0[i].Vowel == myM1[i].Vowel {
			sum += weight[i].Vowel
		}
	}
	return sum / mw.sum
}

// SimMorphs returns similarity between ms0 and ms1.
func (mw *MoraeWeight) SimMorphs(ms0, ms1 Morphs) float64 {
	toMora := func(ms Morphs) []Mora {
		ans := make([]Mora, 0, len(ms))
		for _, m := range ms {
			morae, ok := NewMorae(m.Pronounciation)
			if !ok && m.Pronounciation == "" {
				morae = []Mora{MoraDummy}
			}
			ans = append(ans, morae...)
		}
		return ans
	}
	return mw.Similarity(toMora(ms0), toMora(ms1))
}
<|endoftext|>


<commit_before>package sia

import (
	"errors"
	"net"
	"reflect"
	"strconv"
	"time"
)

const (
	timeout   = time.Second * 5
	maxMsgLen = 1 << 16
)

// A NetAddress contains the information needed to contact a peer over TCP.
type NetAddress struct {
	Host string
	Port uint16
}

// String returns the NetAddress as a string, concatentating the hostname and
// port number.
func (na *NetAddress) String() string {
	return net.JoinHostPort(na.Host, strconv.Itoa(int(na.Port)))
}

// Call establishes a TCP connection to the NetAddress, calls the provided
// function on it, and closes the connection.
func (na *NetAddress) Call(fn func(net.Conn) error) error {
	conn, err := net.DialTimeout("tcp", na.String(), timeout)
	if err != nil {
		return err
	}
	defer conn.Close()
	return fn(conn)
}

// SendVal returns a closure that can be used in conjuction with Call to send
// a value to a NetAddress. It prefixes the encoded data with a header,
// containing the message's type and length
func SendVal(t byte, val interface{}) func(net.Conn) error {
	encVal := Marshal(val)
	encLen := EncUint64(uint64(len(encVal)))
	msg := append([]byte{t},
		append(encLen[:4], encVal...)...)

	return func(conn net.Conn) error {
		_, err := conn.Write(msg)
		return err
	}
}

// TBD
var BootstrapPeers = []NetAddress{}

// A TCPServer sends and receives messages. It also maintains an address book
// of peers to broadcast to and make requests of.
type TCPServer struct {
	net.Listener
	myAddr      NetAddress
	addressbook map[NetAddress]struct{}
	handlerMap  map[byte]func(net.Conn, []byte) error
}

// Broadcast calls the specified function on each peer in the address book.
func (tcps *TCPServer) Broadcast(fn func(net.Conn) error) {
	for addr := range tcps.addressbook {
		addr.Call(fn)
	}
}

// RegisterHandler registers a message type with a message handler. The
// existing handler for that type will be overwritten.
func (tcps *TCPServer) RegisterHandler(t byte, fn func(net.Conn, []byte) error) {
	tcps.handlerMap[t] = fn
}

// RegisterRPC is for simple handlers. A simple handler decodes the message
// data and passes it to fn. fn must have the type signature:
//   func(Type) error
// i.e. a 1-adic function that returns an error.
func (tcps *TCPServer) RegisterRPC(t byte, fn interface{}) error {
	// if fn not correct type, panic
	val, typ := reflect.ValueOf(fn), reflect.TypeOf(fn)
	if typ.Kind() != reflect.Func || typ.NumIn() != 1 ||
		typ.NumOut() != 1 || typ.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
		return errors.New("registered function has wrong type signature")
	}

	// create function:
	sfn := func(_ net.Conn, b []byte) error {
		v := reflect.New(typ.In(0)).Elem()
		if err := Unmarshal(b, v.Interface()); err != nil {
			return err
		}
		if err := val.Call([]reflect.Value{v})[0].Interface(); err != nil {
			return err.(error)
		}
		return nil
	}

	tcps.RegisterHandler(t, sfn)
	return nil
}

// NewTCPServer creates a TCPServer that listens on the specified port.
func NewTCPServer(port uint16) (tcps *TCPServer, err error) {
	tcpServ, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		return
	}
	tcps = &TCPServer{
		Listener:    tcpServ,
		addressbook: make(map[NetAddress]struct{}),
		// default handlers
		handlerMap: map[byte]func(net.Conn, []byte) error{
			'H': sendHostname,
			'P': tcps.sharePeers,
		},
	}

	// spawn listener
	go tcps.listen()
	return
}

// listen runs in the background, accepting incoming connections and serving
// them. listen will return after TCPServer.Close() is called, because the
// Accept() call will fail.
func (tcps *TCPServer) listen() {
	for {
		conn, err := tcps.Accept()
		if err != nil {
			return
		}
		// it is the handler's responsibility to close the connection
		go tcps.handleConn(conn)
	}
}

// handleConn reads header data from a connection, unmarshals the data
// structures it contains, and routes the data to other functions for
// processing.
// TODO: set deadlines?
func (tcps *TCPServer) handleConn(conn net.Conn) {
	defer conn.Close()
	var (
		msgHead []byte = make([]byte, 5)
		msgData []byte // length determined by msgHead
	)
	if n, err := conn.Read(msgHead); err != nil || n != 5 {
		// TODO: log error
		return
	}
	msgLen := DecUint64(msgHead[1:])
	if msgLen > maxMsgLen {
		// TODO: log error
		return
	}
	if msgLen > 0 {
		msgData = make([]byte, msgLen)
		if n, err := conn.Read(msgData); err != nil || uint64(n) != msgLen {
			// TODO: log error
			return
		}
	}

	// call registered handler for this message type
	if fn, ok := tcps.handlerMap[msgHead[0]]; ok {
		fn(conn, msgData)
		// TODO: log error
		// no wait, send the error?
	}
	return
}

// sendHostname replies to the send with the sender's external IP.
func sendHostname(conn net.Conn, _ []byte) error {
	_, err := conn.Write([]byte(conn.RemoteAddr().String()))
	return err
}

// sharePeers transmits at most 'num' peers over the connection.
// TODO: choose random peers?
func (tcps *TCPServer) sharePeers(conn net.Conn, msgData []byte) error {
	if len(msgData) != 1 {
		return errors.New("invalid number of peers")
	}
	num := msgData[0]
	var addrs []NetAddress
	for addr := range tcps.addressbook {
		if num == 0 {
			break
		}
		addrs = append(addrs, addr)
		num--
	}
	_, err := conn.Write(Marshal(addrs))
	return err
}

// Ping returns whether a NetAddress is reachable. It accomplishes this by
// initiating a TCP connection and immediately closes it. This is pretty
// unsophisticated. I'll add a Pong later.
func (tcps *TCPServer) Ping(addr NetAddress) bool {
	conn, err := net.DialTimeout("tcp", addr.String(), timeout)
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

// learnHostname learns the external IP of the TCPServer.
func (tcps *TCPServer) learnHostname(conn net.Conn) (err error) {
	// send hostname request
	if _, err = conn.Write([]byte{'H', 0, 0, 0, 0}); err != nil {
		return
	}
	// read response
	buf := make([]byte, 128)
	n, err := conn.Read(buf)
	if err != nil {
		return
	}
	// TODO: try to ping ourselves?
	host, portStr, err := net.SplitHostPort(string(buf[:n]))
	if err != nil {
		return
	}
	port, err := strconv.Atoi(portStr)
	if err != nil {
		return
	}
	tcps.myAddr = NetAddress{host, uint16(port)}
	return
}

// requestPeers queries a peer for additional peers, and adds any new peers to
// the address book.
func (tcps *TCPServer) requestPeers(conn net.Conn) (err error) {
	// request 10 peers
	if _, err = conn.Write([]byte{'P', 1, 0, 0, 0, 10}); err != nil {
		return
	}
	// read response
	buf := make([]byte, 1024)
	n, err := conn.Read(buf)
	if err != nil {
		return
	}
	var addrs []NetAddress
	if err = Unmarshal(buf[:n], &addrs); err != nil {
		return
	}
	// add peers
	// TODO: make sure we don't add ourself
	for _, addr := range addrs {
		if tcps.Ping(addr) {
			tcps.addressbook[addr] = struct{}{}
		}
	}
	return
}

// Bootstrap discovers the external IP of the TCPServer, requests peers from
// the initial peer list, and announces itself to those peers.
func (tcps *TCPServer) Bootstrap() (err error) {
	// populate initial peer list
	for _, addr := range BootstrapPeers {
		if tcps.Ping(addr) {
			tcps.addressbook[addr] = struct{}{}
		}
	}

	// learn hostname
	for addr := range tcps.addressbook {
		if addr.Call(tcps.learnHostname) == nil {
			break
		}
	}
	// request peers
	// TODO: maybe iterate until we have enough new peers?
	for addr := range tcps.addressbook {
		addr.Call(tcps.requestPeers)
	}
	// TODO: announce ourselves to new peers
	return
}
<commit_msg>add peer announcement<commit_after>package sia

import (
	"errors"
	"net"
	"reflect"
	"strconv"
	"time"
)

const (
	timeout   = time.Second * 5
	maxMsgLen = 1 << 16
)

// A NetAddress contains the information needed to contact a peer over TCP.
type NetAddress struct {
	Host string
	Port uint16
}

// String returns the NetAddress as a string, concatentating the hostname and
// port number.
func (na *NetAddress) String() string {
	return net.JoinHostPort(na.Host, strconv.Itoa(int(na.Port)))
}

// Call establishes a TCP connection to the NetAddress, calls the provided
// function on it, and closes the connection.
func (na *NetAddress) Call(fn func(net.Conn) error) error {
	conn, err := net.DialTimeout("tcp", na.String(), timeout)
	if err != nil {
		return err
	}
	defer conn.Close()
	return fn(conn)
}

// SendVal returns a closure that can be used in conjuction with Call to send
// a value to a NetAddress. It prefixes the encoded data with a header,
// containing the message's type and length
func SendVal(t byte, val interface{}) func(net.Conn) error {
	encVal := Marshal(val)
	encLen := EncUint64(uint64(len(encVal)))
	msg := append([]byte{t},
		append(encLen[:4], encVal...)...)

	return func(conn net.Conn) error {
		_, err := conn.Write(msg)
		return err
	}
}

// TBD
var BootstrapPeers = []NetAddress{}

// A TCPServer sends and receives messages. It also maintains an address book
// of peers to broadcast to and make requests of.
type TCPServer struct {
	net.Listener
	myAddr      NetAddress
	addressbook map[NetAddress]struct{}
	handlerMap  map[byte]func(net.Conn, []byte) error
}

// Broadcast calls the specified function on each peer in the address book.
func (tcps *TCPServer) Broadcast(fn func(net.Conn) error) {
	for addr := range tcps.addressbook {
		addr.Call(fn)
	}
}

// RegisterHandler registers a message type with a message handler. The
// existing handler for that type will be overwritten.
func (tcps *TCPServer) RegisterHandler(t byte, fn func(net.Conn, []byte) error) {
	tcps.handlerMap[t] = fn
}

// RegisterRPC is for simple handlers. A simple handler decodes the message
// data and passes it to fn. fn must have the type signature:
//   func(Type) error
// i.e. a 1-adic function that returns an error.
func (tcps *TCPServer) RegisterRPC(t byte, fn interface{}) error {
	// if fn not correct type, panic
	val, typ := reflect.ValueOf(fn), reflect.TypeOf(fn)
	if typ.Kind() != reflect.Func || typ.NumIn() != 1 ||
		typ.NumOut() != 1 || typ.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
		return errors.New("registered function has wrong type signature")
	}

	// create function:
	sfn := func(_ net.Conn, b []byte) error {
		v := reflect.New(typ.In(0)).Elem()
		if err := Unmarshal(b, v.Interface()); err != nil {
			return err
		}
		if err := val.Call([]reflect.Value{v})[0].Interface(); err != nil {
			return err.(error)
		}
		return nil
	}

	tcps.RegisterHandler(t, sfn)
	return nil
}

// NewTCPServer creates a TCPServer that listens on the specified port.
func NewTCPServer(port uint16) (tcps *TCPServer, err error) {
	tcpServ, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		return
	}
	tcps = &TCPServer{
		Listener:    tcpServ,
		addressbook: make(map[NetAddress]struct{}),
		// default handlers
		handlerMap: map[byte]func(net.Conn, []byte) error{
			'H': sendHostname,
			'P': tcps.sharePeers,
			'A': tcps.addPeer,
		},
	}

	// spawn listener
	go tcps.listen()
	return
}

// listen runs in the background, accepting incoming connections and serving
// them. listen will return after TCPServer.Close() is called, because the
// Accept() call will fail.
func (tcps *TCPServer) listen() {
	for {
		conn, err := tcps.Accept()
		if err != nil {
			return
		}
		// it is the handler's responsibility to close the connection
		go tcps.handleConn(conn)
	}
}

// handleConn reads header data from a connection, unmarshals the data
// structures it contains, and routes the data to other functions for
// processing.
// TODO: set deadlines?
func (tcps *TCPServer) handleConn(conn net.Conn) {
	defer conn.Close()
	var (
		msgHead []byte = make([]byte, 5)
		msgData []byte // length determined by msgHead
	)
	if n, err := conn.Read(msgHead); err != nil || n != 5 {
		// TODO: log error
		return
	}
	msgLen := DecUint64(msgHead[1:])
	if msgLen > maxMsgLen {
		// TODO: log error
		return
	}
	if msgLen > 0 {
		msgData = make([]byte, msgLen)
		if n, err := conn.Read(msgData); err != nil || uint64(n) != msgLen {
			// TODO: log error
			return
		}
	}

	// call registered handler for this message type
	if fn, ok := tcps.handlerMap[msgHead[0]]; ok {
		fn(conn, msgData)
		// TODO: log error
		// no wait, send the error?
	}
	return
}

// sendHostname replies to the send with the sender's external IP.
func sendHostname(conn net.Conn, _ []byte) error {
	_, err := conn.Write([]byte(conn.RemoteAddr().String()))
	return err
}

// sharePeers transmits at most 'num' peers over the connection.
// TODO: choose random peers?
func (tcps *TCPServer) sharePeers(conn net.Conn, msgData []byte) error {
	if len(msgData) != 1 {
		return errors.New("invalid number of peers")
	}
	num := msgData[0]
	var addrs []NetAddress
	for addr := range tcps.addressbook {
		if num == 0 {
			break
		}
		addrs = append(addrs, addr)
		num--
	}
	_, err := conn.Write(Marshal(addrs))
	return err
}

// addPeer adds the connecting peer to its address book
func (tcps *TCPServer) addPeer(conn net.Conn, _ []byte) (err error) {
	host, portStr, err := net.SplitHostPort(conn.RemoteAddr().String())
	if err != nil {
		return
	}
	port, err := strconv.Atoi(portStr)
	if err != nil {
		return
	}
	tcps.addressbook[NetAddress{host, uint16(port)}] = struct{}{}
	return
}

// Ping returns whether a NetAddress is reachable. It accomplishes this by
// initiating a TCP connection and immediately closes it. This is pretty
// unsophisticated. I'll add a Pong later.
func (tcps *TCPServer) Ping(addr NetAddress) bool {
	conn, err := net.DialTimeout("tcp", addr.String(), timeout)
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

// learnHostname learns the external IP of the TCPServer.
func (tcps *TCPServer) learnHostname(conn net.Conn) (err error) {
	// send hostname request
	if _, err = conn.Write([]byte{'H', 0, 0, 0, 0}); err != nil {
		return
	}
	// read response
	buf := make([]byte, 128)
	n, err := conn.Read(buf)
	if err != nil {
		return
	}
	// TODO: try to ping ourselves?
	host, portStr, err := net.SplitHostPort(string(buf[:n]))
	if err != nil {
		return
	}
	port, err := strconv.Atoi(portStr)
	if err != nil {
		return
	}
	tcps.myAddr = NetAddress{host, uint16(port)}
	return
}

// requestPeers queries a peer for additional peers, and adds any new peers to
// the address book.
func (tcps *TCPServer) requestPeers(conn net.Conn) (err error) {
	// request 10 peers
	if _, err = conn.Write([]byte{'P', 1, 0, 0, 0, 10}); err != nil {
		return
	}
	// read response
	buf := make([]byte, 1024)
	n, err := conn.Read(buf)
	if err != nil {
		return
	}
	var addrs []NetAddress
	if err = Unmarshal(buf[:n], &addrs); err != nil {
		return
	}
	// add peers
	// TODO: make sure we don't add ourself
	for _, addr := range addrs {
		if tcps.Ping(addr) {
			tcps.addressbook[addr] = struct{}{}
		}
	}
	return
}

// addMe announces the TCPServer's NetAddress to a peer
func (tcps *TCPServer) addMe(conn net.Conn) error {
	_, err := conn.Write([]byte{'A', 0, 0, 0, 0})
	return err
}

// Bootstrap discovers the external IP of the TCPServer, requests peers from
// the initial peer list, and announces itself to those peers.
func (tcps *TCPServer) Bootstrap() (err error) {
	// populate initial peer list
	for _, addr := range BootstrapPeers {
		if tcps.Ping(addr) {
			tcps.addressbook[addr] = struct{}{}
		}
	}

	// learn hostname
	for addr := range tcps.addressbook {
		if addr.Call(tcps.learnHostname) == nil {
			break
		}
	}

	// request peers
	// TODO: maybe iterate until we have enough new peers?
	tcps.Broadcast(tcps.requestPeers)

	// announce ourselves to new peers
	tcps.Broadcast(tcps.addMe)

	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package hdkeychain_test

import (
	"fmt"

	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/dcrutil/hdkeychain"
)

// This example demonstrates how to generate a cryptographically random seed
// then use it to create a new master node (extended key).
func ExampleNewMaster() {
	// Generate a random seed at the recommended length.
	seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Generate a new master node using the seed.
	key, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Show that the generated master node extended key is private.
	fmt.Println("Private Extended Key?:", key.IsPrivate())

	// Output:
	// Private Extended Key?: true
}

// This example demonstrates the default hierarchical deterministic wallet
// layout as described in BIP0032.
func Example_defaultWalletLayout() {
	// The default wallet layout described in BIP0032 is:
	//
	// Each account is composed of two keypair chains: an internal and an
	// external one. The external keychain is used to generate new public
	// addresses, while the internal keychain is used for all other
	// operations (change addresses, generation addresses, ..., anything
	// that doesn't need to be communicated).
	//
	//   * m/iH/0/k
	//     corresponds to the k'th keypair of the external chain of account
	//     number i of the HDW derived from master m.
	//   * m/iH/1/k
	//     corresponds to the k'th keypair of the internal chain of account
	//     number i of the HDW derived from master m.

	// Ordinarily this would either be read from some encrypted source
	// and be decrypted or generated as the NewMaster example shows, but
	// for the purposes of this example, the private exteded key for the
	// master node is being hard coded here.
	master := "dprv3hCznBesA6jBushjx7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkd" +
		"RMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA"

	// Start by getting an extended key instance for the master node.
	// This gives the path:
	//   m
	masterKey, err := hdkeychain.NewKeyFromString(master)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the extended key for account 0.  This gives the path:
	//   m/0H
	acct0, err := masterKey.Child(hdkeychain.HardenedKeyStart + 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the extended key for the account 0 external chain.  This
	// gives the path:
	//   m/0H/0
	acct0Ext, err := acct0.Child(0)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the extended key for the account 0 internal chain.  This gives
	// the path:
	//   m/0H/1
	acct0Int, err := acct0.Child(1)
	if err != nil {
		fmt.Println(err)
		return
	}

	// At this point, acct0Ext and acct0Int are ready to derive the keys for
	// the external and internal wallet chains.

	// Derive the 10th extended key for the account 0 external chain.  This
	// gives the path:
	//   m/0H/0/10
	acct0Ext10, err := acct0Ext.Child(10)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the 1st extended key for the account 0 internal chain.  This
	// gives the path:
	//   m/0H/1/0
	acct0Int0, err := acct0Int.Child(0)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Get and show the address associated with the extended keys for the
	// main decred network.
	acct0ExtAddr, err := acct0Ext10.Address(&chaincfg.MainNetParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	acct0IntAddr, err := acct0Int0.Address(&chaincfg.MainNetParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Account 0 External Address 10:", acct0ExtAddr)
	fmt.Println("Account 0 Internal Address 0:", acct0IntAddr)

	// Output:
	// Account 0 External Address 10: DshMmJ3bfvMDdk1mkXRD3x5xDuPwSxoYGfi
	// Account 0 Internal Address 0: DsoTyktAyEDkYpgKSex6zx5rrkFDi2gAsHr
}

// This example demonstrates the audits use case in BIP0032.
func Example_audits() {
	// The audits use case described in BIP0032 is:
	//
	// In case an auditor needs full access to the list of incoming and
	// outgoing payments, one can share all account public extended keys.
	// This will allow the auditor to see all transactions from and to the
	// wallet, in all accounts, but not a single secret key.
	//
	//   * N(m/*)
	//   corresponds to the neutered master extended key (also called
	//   the master public extended key)

	// Ordinarily this would either be read from some encrypted source
	// and be decrypted or generated as the NewMaster example shows, but
	// for the purposes of this example, the private exteded key for the
	// master node is being hard coded here.
	master := "dprv3hCznBesA6jBushjx7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkd" +
		"RMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA"

	// Start by getting an extended key instance for the master node.
	// This gives the path:
	//   m
	masterKey, err := hdkeychain.NewKeyFromString(master)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Neuter the master key to generate a master public extended key.  This
	// gives the path:
	//   N(m/*)
	masterPubKey, err := masterKey.Neuter()
	if err != nil {
		fmt.Println(err)
		return
	}

	// Share the master public extended key with the auditor.
	mpks, err := masterPubKey.String()
	if err != nil {
		panic("unexpected error creating string of extended public key")
	}
	fmt.Println("Audit key N(m/*):", mpks)

	// Output:
	// Audit key N(m/*): dpubZ9169KDAEUnypHbWCe2Vu5TxGEcqJeNeX6XCYFU1fqw2iQZK7fsMhzsEFArbLmyUdprUw9aXHneUNd92bjc31TqC6sUduMY6PK2z4JXDS8j
}
<commit_msg>hdkeychain: Work around go vet issue with examples.<commit_after>// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package hdkeychain_test

import (
	"fmt"

	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/dcrutil/hdkeychain"
)

// This example demonstrates how to generate a cryptographically random seed
// then use it to create a new master node (extended key).
func Example_newMaster() {
	// Generate a random seed at the recommended length.
	seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Generate a new master node using the seed.
	key, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Show that the generated master node extended key is private.
	fmt.Println("Private Extended Key?:", key.IsPrivate())

	// Output:
	// Private Extended Key?: true
}

// This example demonstrates the default hierarchical deterministic wallet
// layout as described in BIP0032.
func Example_defaultWalletLayout() {
	// The default wallet layout described in BIP0032 is:
	//
	// Each account is composed of two keypair chains: an internal and an
	// external one. The external keychain is used to generate new public
	// addresses, while the internal keychain is used for all other
	// operations (change addresses, generation addresses, ..., anything
	// that doesn't need to be communicated).
	//
	//   * m/iH/0/k
	//     corresponds to the k'th keypair of the external chain of account
	//     number i of the HDW derived from master m.
	//   * m/iH/1/k
	//     corresponds to the k'th keypair of the internal chain of account
	//     number i of the HDW derived from master m.

	// Ordinarily this would either be read from some encrypted source
	// and be decrypted or generated as the NewMaster example shows, but
	// for the purposes of this example, the private exteded key for the
	// master node is being hard coded here.
	master := "dprv3hCznBesA6jBushjx7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkd" +
		"RMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA"

	// Start by getting an extended key instance for the master node.
	// This gives the path:
	//   m
	masterKey, err := hdkeychain.NewKeyFromString(master)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the extended key for account 0.  This gives the path:
	//   m/0H
	acct0, err := masterKey.Child(hdkeychain.HardenedKeyStart + 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the extended key for the account 0 external chain.  This
	// gives the path:
	//   m/0H/0
	acct0Ext, err := acct0.Child(0)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the extended key for the account 0 internal chain.  This gives
	// the path:
	//   m/0H/1
	acct0Int, err := acct0.Child(1)
	if err != nil {
		fmt.Println(err)
		return
	}

	// At this point, acct0Ext and acct0Int are ready to derive the keys for
	// the external and internal wallet chains.

	// Derive the 10th extended key for the account 0 external chain.  This
	// gives the path:
	//   m/0H/0/10
	acct0Ext10, err := acct0Ext.Child(10)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Derive the 1st extended key for the account 0 internal chain.  This
	// gives the path:
	//   m/0H/1/0
	acct0Int0, err := acct0Int.Child(0)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Get and show the address associated with the extended keys for the
	// main decred network.
	acct0ExtAddr, err := acct0Ext10.Address(&chaincfg.MainNetParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	acct0IntAddr, err := acct0Int0.Address(&chaincfg.MainNetParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Account 0 External Address 10:", acct0ExtAddr)
	fmt.Println("Account 0 Internal Address 0:", acct0IntAddr)

	// Output:
	// Account 0 External Address 10: DshMmJ3bfvMDdk1mkXRD3x5xDuPwSxoYGfi
	// Account 0 Internal Address 0: DsoTyktAyEDkYpgKSex6zx5rrkFDi2gAsHr
}

// This example demonstrates the audits use case in BIP0032.
func Example_audits() {
	// The audits use case described in BIP0032 is:
	//
	// In case an auditor needs full access to the list of incoming and
	// outgoing payments, one can share all account public extended keys.
	// This will allow the auditor to see all transactions from and to the
	// wallet, in all accounts, but not a single secret key.
	//
	//   * N(m/*)
	//   corresponds to the neutered master extended key (also called
	//   the master public extended key)

	// Ordinarily this would either be read from some encrypted source
	// and be decrypted or generated as the NewMaster example shows, but
	// for the purposes of this example, the private exteded key for the
	// master node is being hard coded here.
	master := "dprv3hCznBesA6jBushjx7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkd" +
		"RMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA"

	// Start by getting an extended key instance for the master node.
	// This gives the path:
	//   m
	masterKey, err := hdkeychain.NewKeyFromString(master)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Neuter the master key to generate a master public extended key.  This
	// gives the path:
	//   N(m/*)
	masterPubKey, err := masterKey.Neuter()
	if err != nil {
		fmt.Println(err)
		return
	}

	// Share the master public extended key with the auditor.
	mpks, err := masterPubKey.String()
	if err != nil {
		panic("unexpected error creating string of extended public key")
	}
	fmt.Println("Audit key N(m/*):", mpks)

	// Output:
	// Audit key N(m/*): dpubZ9169KDAEUnypHbWCe2Vu5TxGEcqJeNeX6XCYFU1fqw2iQZK7fsMhzsEFArbLmyUdprUw9aXHneUNd92bjc31TqC6sUduMY6PK2z4JXDS8j
}
<|endoftext|>


<commit_before>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/briandowns/spinner"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/disc"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

const (
	version = "0.2.2"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

var (
	pNames = providerNames()
	req    = make(chan string, 1)
	nxt    = make(chan struct{}, 1)
	spin   = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	eArgs  = os.Args
	args   string
	prompt string
	cPName string
	noIf   bool = true
	cfg    cli.Config
	nsr    *ns.Request
	c      *cli.Readline

	// register looking glass hosts
	providers = map[string]Provider{
		"telia":  new(lg.Telia),  // telia
		"level3": new(lg.Level3), // level3
		"cogent": new(lg.Cogent), //cogent
	}

	// map cmd to function
	cmdFunc = map[string]func(){
		"web":     web,         // web dashboard
		"dump":    dump,        // dump traffic
		"disc":    discovery,   // network discovery
		"scan":    scanPorts,   // network scan
		"mode":    mode,        // editor mode
		"ping":    pingQuery,   // ping
		"trace":   trace,       // trace route
		"bgp":     BGP,         // BGP
		"whois":   whoisLookup, // whois / dns lookup
		"peering": peeringDB,   // peering DB
		"hping":   hping,       // hping
		"dig":     dig,         // dig
		"node":    node,        // change node
		"connect": connect,     // connect to a country or LG
		"local":   local,       // local
		"help":    help,        // help
		"exit":    cleanUp,     // clean up
		"quit":    cleanUp,     // clean up
		"show":    show,        // show config
		"set":     setConfig,   // set config
		"lg":      setLG,       // prepare looking glass
		"ns":      setNS,       // prepare name server
	}
)

// init
func init() {
	// load configuration
	cfg = cli.LoadConfig()
	// with interface
	if len(eArgs) == 1 {
		// initialize cli
		c = cli.Init("local", version)
		go c.Run(req, nxt)
		// start web server
		go httpd.Run(cfg)
		// set interface enabled
		noIf = false
	}
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// set default provider, promot
	cPName = "local"
	prompt = "local"
}

func main() {
	// command line w/o interface
	if noIf {
		cmd := eArgs[1]
		args = strings.Join(eArgs[2:], " ")
		if f, ok := cmdFunc[cmd]; ok {
			f()
		} else {
			println("Invalid command please try mylg help")
		}
		return
	}
	// command like w/ interface
LOOP:
	for {
		select {
		case request, ok := <-req:
			if !ok {
				break LOOP
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDReg.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			if f, ok := cmdFunc[cmd]; ok {
				f()
			} else {
				println("Invalid command please try help")
			}
			c.Next()
		}
	}
}

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// dig gets dig info
func dig() {
	if ok := nsr.SetOptions(args, prompt); ok {
		nsr.Dig()
	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, fmt.Sprintf("http://%s:%d", cfg.Web.Address, cfg.Web.Port))
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if p == nil || err != nil {
		return
	}
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace, err := icmp.NewTrace(args)
		if err != nil {
			println(err.Error())
		}
		if trace == nil {
			break
		}
		trace.PrintPretty()
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	// it should work at local mode
	if cPName != "local" {
		return
	}
	p, err := ping.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingQuery runs ping command (local/LG)
func pingQuery() {
	if cPName == "local" {
		pingLocal()
	} else {
		pingLG()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	p, err := icmp.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	}
	if p == nil {
		return
	}
	if !p.IsCIDR() {
		resp := p.Run()
		p.PrintPretty(resp)
	}
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}

// discovery handles disc command
func discovery() {
	var (
		wg sync.WaitGroup
		//ts = time.Now()
	)

	d := disc.New(args)
	// help requested
	if d == nil {
		return
	}

	spin.Prefix = "please wait "
	spin.Start()

	// load OUI async
	go func() {
		wg.Add(1)
		d.LoadOUI()
		wg.Done()
	}()
	d.PingLan()
	//s := 10 - int(time.Since(ts).Seconds())
	time.Sleep(5 * time.Second)
	if err := d.GetARPTable(); err != nil {
		println(err.Error())
		return
	}
	wg.Wait()
	spin.Stop()

	println("Network LAN Discovery")
	d.PrintPretty()
}

// setConfig
func setConfig() {
	cli.SetConfig(args, &cfg)
}

// showConfig
func show() {
	if args == "config" {
		cli.ShowConfig(&cfg)
	}
}

// setLG set lg prompt and completer
func setLG() {
	c.SetPrompt("lg")
	c.UpdateCompleter("connect", pNames)
}

// setNS set ns prompt and update completers
func setNS() {
	c.UpdateCompleter("connect", nsr.CountryList())
	c.UpdateCompleter("node", []string{})
	c.SetPrompt("ns")
}

// peeringDB gets peer info
func peeringDB() {
	peeringdb.Search(args)
}

// whoisLookup gets ANS/Prefix info
func whoisLookup() {
	whois.Lookup(args)
}

// local set prompts to local
func local() {
	nsr.Local()
	cPName = "local"
	c.SetPrompt(cPName)
}

// cleanUp
func cleanUp() {
	c.Close(nxt)
	close(req)
}

// help
func help() {
	if noIf {
		// without command line
		h := `
              ***** TRY IT WITHOUT ANYTHING TO HAVE INTERFACE *****
        Usage:
              mylg [command] [args...]

              Available commands:

              ping                        ping ip address or domain name
              dig                         name server looking up
              whois                       resolve AS number/IP/CIDR to holder (provides by ripe ncc)
              hping                       Ping through HTTP/HTTPS w/ GET/HEAD methods
              scan                        scan tcp ports (you can provide range >scan host minport maxport)
              dump                        prints out a description of the contents of packets on a network interface
              disc                        discover all the devices on a LAN
              peering                     peering information (provides by peeringdb.com)
              varsion                     shows mylg version

        Example:
              mylg whois 8.8.8.8
              mylg scan 127.0.0.1
              mylg dig google.com +trace
		`
		fmt.Println(h)
	} else {
		// with command line interface

		c.Help()
	}
}
<commit_msg>bump version to 0.2.3<commit_after>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/briandowns/spinner"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/disc"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

const (
	version = "0.2.3"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

var (
	pNames = providerNames()
	req    = make(chan string, 1)
	nxt    = make(chan struct{}, 1)
	spin   = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	eArgs  = os.Args
	args   string
	prompt string
	cPName string
	noIf   bool = true
	cfg    cli.Config
	nsr    *ns.Request
	c      *cli.Readline

	// register looking glass hosts
	providers = map[string]Provider{
		"telia":  new(lg.Telia),  // telia
		"level3": new(lg.Level3), // level3
		"cogent": new(lg.Cogent), //cogent
	}

	// map cmd to function
	cmdFunc = map[string]func(){
		"web":     web,         // web dashboard
		"dump":    dump,        // dump traffic
		"disc":    discovery,   // network discovery
		"scan":    scanPorts,   // network scan
		"mode":    mode,        // editor mode
		"ping":    pingQuery,   // ping
		"trace":   trace,       // trace route
		"bgp":     BGP,         // BGP
		"whois":   whoisLookup, // whois / dns lookup
		"peering": peeringDB,   // peering DB
		"hping":   hping,       // hping
		"dig":     dig,         // dig
		"node":    node,        // change node
		"connect": connect,     // connect to a country or LG
		"local":   local,       // local
		"help":    help,        // help
		"exit":    cleanUp,     // clean up
		"quit":    cleanUp,     // clean up
		"show":    show,        // show config
		"set":     setConfig,   // set config
		"lg":      setLG,       // prepare looking glass
		"ns":      setNS,       // prepare name server
	}
)

// init
func init() {
	// load configuration
	cfg = cli.LoadConfig()
	// with interface
	if len(eArgs) == 1 {
		// initialize cli
		c = cli.Init("local", version)
		go c.Run(req, nxt)
		// start web server
		go httpd.Run(cfg)
		// set interface enabled
		noIf = false
	}
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// set default provider, promot
	cPName = "local"
	prompt = "local"
}

func main() {
	// command line w/o interface
	if noIf {
		cmd := eArgs[1]
		args = strings.Join(eArgs[2:], " ")
		if f, ok := cmdFunc[cmd]; ok {
			f()
		} else {
			println("Invalid command please try mylg help")
		}
		return
	}
	// command like w/ interface
LOOP:
	for {
		select {
		case request, ok := <-req:
			if !ok {
				break LOOP
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDReg.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			if f, ok := cmdFunc[cmd]; ok {
				f()
			} else {
				println("Invalid command please try help")
			}
			c.Next()
		}
	}
}

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// dig gets dig info
func dig() {
	if ok := nsr.SetOptions(args, prompt); ok {
		nsr.Dig()
	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, fmt.Sprintf("http://%s:%d", cfg.Web.Address, cfg.Web.Port))
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if p == nil || err != nil {
		return
	}
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace, err := icmp.NewTrace(args)
		if err != nil {
			println(err.Error())
		}
		if trace == nil {
			break
		}
		trace.PrintPretty()
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	// it should work at local mode
	if cPName != "local" {
		return
	}
	p, err := ping.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingQuery runs ping command (local/LG)
func pingQuery() {
	if cPName == "local" {
		pingLocal()
	} else {
		pingLG()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	p, err := icmp.NewPing(args, cfg)
	if err != nil {
		println(err.Error())
	}
	if p == nil {
		return
	}
	if !p.IsCIDR() {
		resp := p.Run()
		p.PrintPretty(resp)
	}
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args, cfg)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}

// discovery handles disc command
func discovery() {
	var (
		wg sync.WaitGroup
		//ts = time.Now()
	)

	d := disc.New(args)
	// help requested
	if d == nil {
		return
	}

	spin.Prefix = "please wait "
	spin.Start()

	// load OUI async
	go func() {
		wg.Add(1)
		d.LoadOUI()
		wg.Done()
	}()
	d.PingLan()
	//s := 10 - int(time.Since(ts).Seconds())
	time.Sleep(5 * time.Second)
	if err := d.GetARPTable(); err != nil {
		println(err.Error())
		return
	}
	wg.Wait()
	spin.Stop()

	println("Network LAN Discovery")
	d.PrintPretty()
}

// setConfig
func setConfig() {
	cli.SetConfig(args, &cfg)
}

// showConfig
func show() {
	if args == "config" {
		cli.ShowConfig(&cfg)
	}
}

// setLG set lg prompt and completer
func setLG() {
	c.SetPrompt("lg")
	c.UpdateCompleter("connect", pNames)
}

// setNS set ns prompt and update completers
func setNS() {
	c.UpdateCompleter("connect", nsr.CountryList())
	c.UpdateCompleter("node", []string{})
	c.SetPrompt("ns")
}

// peeringDB gets peer info
func peeringDB() {
	peeringdb.Search(args)
}

// whoisLookup gets ANS/Prefix info
func whoisLookup() {
	whois.Lookup(args)
}

// local set prompts to local
func local() {
	nsr.Local()
	cPName = "local"
	c.SetPrompt(cPName)
}

// cleanUp
func cleanUp() {
	c.Close(nxt)
	close(req)
}

// help
func help() {
	if noIf {
		// without command line
		h := `
              ***** TRY IT WITHOUT ANYTHING TO HAVE INTERFACE *****
        Usage:
              mylg [command] [args...]

              Available commands:

              ping                        ping ip address or domain name
              dig                         name server looking up
              whois                       resolve AS number/IP/CIDR to holder (provides by ripe ncc)
              hping                       Ping through HTTP/HTTPS w/ GET/HEAD methods
              scan                        scan tcp ports (you can provide range >scan host minport maxport)
              dump                        prints out a description of the contents of packets on a network interface
              disc                        discover all the devices on a LAN
              peering                     peering information (provides by peeringdb.com)
              varsion                     shows mylg version

        Example:
              mylg whois 8.8.8.8
              mylg scan 127.0.0.1
              mylg dig google.com +trace
		`
		fmt.Println(h)
	} else {
		// with command line interface

		c.Help()
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The fleet Authors
//
// 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 agent

import (
	"fmt"
	"path"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/log"
	"github.com/coreos/fleet/machine"
)

type AgentState struct {
	MState *machine.MachineState
	Units  map[string]*job.Unit
}

func NewAgentState(ms *machine.MachineState) *AgentState {
	return &AgentState{
		MState: ms,
		Units:  make(map[string]*job.Unit),
	}
}

func (as *AgentState) unitScheduled(name string) bool {
	return as.Units[name] != nil
}

// HasConflict determines whether there are any known conflicts with the given Unit
func (as *AgentState) HasConflict(pUnitName string, pConflicts []string) (found bool, conflict string) {
	for _, eUnit := range as.Units {
		if pUnitName == eUnit.Name {
			continue
		}

		for _, pConflict := range pConflicts {
			if globMatches(pConflict, eUnit.Name) {
				found = true
				conflict = eUnit.Name
				return
			}
		}

		for _, eConflict := range eUnit.Conflicts() {
			if globMatches(eConflict, pUnitName) {
				found = true
				conflict = eUnit.Name
				return
			}
		}
	}

	return
}

// hasReplace determines whether there are any known replaces with the given Unit
func (as *AgentState) hasReplace(pUnitName string, pReplaces []string) (found bool, replace string) {
	for _, eUnit := range as.Units {
		foundPrepl := false
		foundErepl := false
		retStr := ""

		if pUnitName == eUnit.Name {
			continue
		}

		for _, pReplace := range pReplaces {
			if globMatches(pReplace, eUnit.Name) {
				foundPrepl = true
				retStr = eUnit.Name
				break
			}
		}

		for _, eReplace := range eUnit.Replaces() {
			if globMatches(eReplace, pUnitName) {
				foundErepl = true
				retStr = eUnit.Name
				break
			}
		}

		// Only 1 of 2 matches must be found. If both matches are found,
		// it means it's a circular replace situation, which could result in
		// an infinite loop. So ignore such replace options.
		if (foundPrepl && foundErepl) || (!foundPrepl && !foundErepl) {
			continue
		} else {
			found = true
			replace = retStr
			return
		}
	}

	return
}

func globMatches(pattern, target string) bool {
	matched, err := path.Match(pattern, target)
	if err != nil {
		log.Debugf("Received error while matching pattern '%s': %v", pattern, err)
	}
	return matched
}

// AbleToRun determines if an Agent can run the provided Job based on
// the Agent's current state. A boolean indicating whether this is the
// case or not is returned. The following criteria is used:
//   - Agent must meet the Job's machine target requirement (if any)
//   - Agent must have all of the Job's required metadata (if any)
//   - Agent must have all required Peers of the Job scheduled locally (if any)
//   - Job must not conflict with any other Units scheduled to the agent
//   - Job must specially handle replaced units to be rescheduled
func (as *AgentState) AbleToRun(j *job.Job) (jobAction job.JobAction, errstr string) {
	if tgt, ok := j.RequiredTarget(); ok && !as.MState.MatchID(tgt) {
		return job.JobActionUnschedule, fmt.Sprintf("agent ID %q does not match required %q", as.MState.ID, tgt)
	}

	metadata := j.RequiredTargetMetadata()
	if len(metadata) != 0 {
		if !machine.HasMetadata(as.MState, metadata) {
			return job.JobActionUnschedule, "local Machine metadata insufficient"
		}
	}

	peers := j.Peers()
	if len(peers) != 0 {
		for _, peer := range peers {
			if !as.unitScheduled(peer) {
				return job.JobActionUnschedule, fmt.Sprintf("required peer Unit(%s) is not scheduled locally", peer)
			}
		}
	}

	if cExists, cJobName := as.HasConflict(j.Name, j.Conflicts()); cExists {
		return job.JobActionUnschedule, fmt.Sprintf("found conflict with locally-scheduled Unit(%s)", cJobName)
	}

	// Handle Replace option specially for rescheduling the unit
	if cExists, cJobName := as.hasReplace(j.Name, j.Replaces()); cExists {
		return job.JobActionReschedule, fmt.Sprintf("found replace with locally-scheduled Unit(%s)", cJobName)
	}

	return job.JobActionSchedule, ""
}

func (as *AgentState) GetReplacedUnit(j *job.Job) (string, error) {
	cExists, replaced := as.hasReplace(j.Name, j.Replaces())
	if !cExists {
		return "", fmt.Errorf("cannot find units to be replaced for Unit(%s)", j.Name)
	}
	return replaced, nil
}
<commit_msg>agent: allow HasConflict() to handle multiple values defined in Conflicts<commit_after>// Copyright 2014 The fleet Authors
//
// 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 agent

import (
	"fmt"
	"path"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/log"
	"github.com/coreos/fleet/machine"
)

type AgentState struct {
	MState *machine.MachineState
	Units  map[string]*job.Unit
}

func NewAgentState(ms *machine.MachineState) *AgentState {
	return &AgentState{
		MState: ms,
		Units:  make(map[string]*job.Unit),
	}
}

func (as *AgentState) unitScheduled(name string) bool {
	return as.Units[name] != nil
}

func hasStringInSlice(inSlice []string, unitName string) bool {
	for _, elem := range inSlice {
		if globMatches(elem, unitName) {
			return true
		}
	}
	return false
}

// HasConflict determines whether there are any known conflicts with the given Unit
func (as *AgentState) HasConflict(pUnitName string, pConflicts []string) (bool, []string) {
	found := false
	conflicts := []string{}

	for _, eUnit := range as.Units {
		if pUnitName == eUnit.Name {
			continue
		}

		if hasStringInSlice(eUnit.Conflicts(), pUnitName) {
			conflicts = append(conflicts, pUnitName)
			found = true
			break
		}
		if hasStringInSlice(pConflicts, eUnit.Name) {
			conflicts = append(conflicts, eUnit.Name)
			found = true
			break
		}
	}

	if !found {
		return false, []string{}
	}

	return true, conflicts
}

// hasReplace determines whether there are any known replaces with the given Unit
func (as *AgentState) hasReplace(pUnitName string, pReplaces []string) (found bool, replace string) {
	for _, eUnit := range as.Units {
		foundPrepl := false
		foundErepl := false
		retStr := ""

		if pUnitName == eUnit.Name {
			continue
		}

		for _, pReplace := range pReplaces {
			if globMatches(pReplace, eUnit.Name) {
				foundPrepl = true
				retStr = eUnit.Name
				break
			}
		}

		for _, eReplace := range eUnit.Replaces() {
			if globMatches(eReplace, pUnitName) {
				foundErepl = true
				retStr = eUnit.Name
				break
			}
		}

		// Only 1 of 2 matches must be found. If both matches are found,
		// it means it's a circular replace situation, which could result in
		// an infinite loop. So ignore such replace options.
		if (foundPrepl && foundErepl) || (!foundPrepl && !foundErepl) {
			continue
		} else {
			found = true
			replace = retStr
			return
		}
	}

	return
}

func globMatches(pattern, target string) bool {
	matched, err := path.Match(pattern, target)
	if err != nil {
		log.Debugf("Received error while matching pattern '%s': %v", pattern, err)
	}
	return matched
}

// AbleToRun determines if an Agent can run the provided Job based on
// the Agent's current state. A boolean indicating whether this is the
// case or not is returned. The following criteria is used:
//   - Agent must meet the Job's machine target requirement (if any)
//   - Agent must have all of the Job's required metadata (if any)
//   - Agent must have all required Peers of the Job scheduled locally (if any)
//   - Job must not conflict with any other Units scheduled to the agent
//   - Job must specially handle replaced units to be rescheduled
func (as *AgentState) AbleToRun(j *job.Job) (jobAction job.JobAction, errstr string) {
	if tgt, ok := j.RequiredTarget(); ok && !as.MState.MatchID(tgt) {
		return job.JobActionUnschedule, fmt.Sprintf("agent ID %q does not match required %q", as.MState.ID, tgt)
	}

	metadata := j.RequiredTargetMetadata()
	if len(metadata) != 0 {
		if !machine.HasMetadata(as.MState, metadata) {
			return job.JobActionUnschedule, "local Machine metadata insufficient"
		}
	}

	peers := j.Peers()
	if len(peers) != 0 {
		for _, peer := range peers {
			if !as.unitScheduled(peer) {
				return job.JobActionUnschedule, fmt.Sprintf("required peer Unit(%s) is not scheduled locally", peer)
			}
		}
	}

	if cExists, cJobName := as.HasConflict(j.Name, j.Conflicts()); cExists {
		return job.JobActionUnschedule, fmt.Sprintf("found conflict with locally-scheduled Unit(%s)", cJobName)
	}

	// Handle Replace option specially for rescheduling the unit
	if cExists, cJobName := as.hasReplace(j.Name, j.Replaces()); cExists {
		return job.JobActionReschedule, fmt.Sprintf("found replace with locally-scheduled Unit(%s)", cJobName)
	}

	return job.JobActionSchedule, ""
}

func (as *AgentState) GetReplacedUnit(j *job.Job) (string, error) {
	cExists, replaced := as.hasReplace(j.Name, j.Replaces())
	if !cExists {
		return "", fmt.Errorf("cannot find units to be replaced for Unit(%s)", j.Name)
	}
	return replaced, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Bertrand Janin <b@janin.com>
// Use of this source code is governed by the ISC license in the LICENSE file.

package main

import (
	"regexp"
	"strings"
	"time"
	"strconv"
	"fmt"
)

var (
	reSoundBoard = regexp.MustCompile(`^(?:\w+ )?(\w+)(?:(?:\s+for)?\s+([0-9]+))?`)
	reAddressed = regexp.MustCompile(`^(\w+)[:,.]+\s+(.*)`)
)

type SoundBoardModule struct{}

// Returns the PRIVMSG without the nickname prefix if any, if the message was
// not addressed to this bot, it returns an empty string.
func AddressedToMe(msg string) string {
	tokens := reAddressed.FindStringSubmatch(msg)

	if tokens[1] == cmd.Nickname {
		return tokens[2]
	}

	return ""
}

func getTune(msg string) (string, uint64) {
	tokens := reSoundBoard.FindStringSubmatch(msg)
	if tokens == nil {
		return "", 0
	}

	tune := strings.ToLower(tokens[1])
	duration, err := strconv.ParseUint(tokens[2], 10, 8)
	if err != nil {
		// That really shouldn't happen since the regexp should only
		// capture uint but we're being cautious.
		return "", 0
	}

	return tune, duration
}

func formatPlayTuneCommand(filename string, duration uint64) string {
	if duration > 0 {
		return fmt.Sprintf("play-tune %s %d", filename, duration)
	} else {
		return fmt.Sprintf("play-tune %s", filename)
	}
}

func playTune(where string, tune string, duration uint64) {
	switch tune {
	case "jeopardy":
		privAction(where, "queues some elevator music...")
		sendToMinion("play-tune jeopardy.mp3")
		sendToMinion(formatPlayTuneCommand("jeopardy.mp3", duration))
	case "africa":
		sendToMinion(formatPlayTuneCommand("africa.ogg", duration))
		time.Sleep(2 * time.Second)
		privAction(where, "hears the drums echoing tonight,")
		time.Sleep(5 * time.Second)
		privMsg(where, "But she hears only whispers of some quiet conversation")
		time.Sleep(9 * time.Second)
		privMsg(where, "She's coming in the 12:30 flight")
		time.Sleep(3 * time.Second)
		privMsg(where, "The moonlit wings reflect the stars that guide me towards salvation")
	case "wagner":
		sendToMinion(formatPlayTuneCommand("wagner.ogg", duration))
	case "nuke":
		sendToMinion(formatPlayTuneCommand("nuke_ready.ogg", duration))
	case "energy":
		sendToMinion(formatPlayTuneCommand("energy.ogg", duration))
	}
}

func (module SoundBoardModule) PrivMsg(nick, where, msg string, isAction bool) {
	if msg = AddressedToMe(msg); msg == "" {
		return
	}

	tune, duration := getTune(msg)
	if tune == "" {
		return
	}

	playTune(where, tune, duration)
}

func (module SoundBoardModule) Init() {
}
<commit_msg>Add check on regexp return.<commit_after>// Copyright (c) 2014 Bertrand Janin <b@janin.com>
// Use of this source code is governed by the ISC license in the LICENSE file.

package main

import (
	"regexp"
	"strings"
	"time"
	"strconv"
	"fmt"
)

var (
	reSoundBoard = regexp.MustCompile(`^(?:\w+ )?(\w+)(?:(?:\s+for)?\s+([0-9]+))?`)
	reAddressed = regexp.MustCompile(`^(\w+)[:,.]+\s+(.*)`)
)

type SoundBoardModule struct{}

// Returns the PRIVMSG without the nickname prefix if any, if the message was
// not addressed to this bot, it returns an empty string.
func AddressedToMe(msg string) string {
	tokens := reAddressed.FindStringSubmatch(msg)
	if tokens == nil {
		return ""
	}

	if tokens[1] == cmd.Nickname {
		return tokens[2]
	}

	return ""
}

func getTune(msg string) (string, uint64) {
	tokens := reSoundBoard.FindStringSubmatch(msg)
	if tokens == nil {
		return "", 0
	}

	tune := strings.ToLower(tokens[1])
	duration, err := strconv.ParseUint(tokens[2], 10, 8)
	if err != nil {
		// That really shouldn't happen since the regexp should only
		// capture uint but we're being cautious.
		return tune, 0
	}

	return tune, duration
}

func formatPlayTuneCommand(filename string, duration uint64) string {
	if duration > 0 {
		return fmt.Sprintf("play-tune %s %d", filename, duration)
	} else {
		return fmt.Sprintf("play-tune %s", filename)
	}
}

func playTune(where string, tune string, duration uint64) {
	switch tune {
	case "jeopardy":
		privAction(where, "queues some elevator music...")
		sendToMinion("play-tune jeopardy.mp3")
		sendToMinion(formatPlayTuneCommand("jeopardy.mp3", duration))
	case "africa":
		sendToMinion(formatPlayTuneCommand("africa.ogg", duration))
		time.Sleep(2 * time.Second)
		privAction(where, "hears the drums echoing tonight,")
		time.Sleep(5 * time.Second)
		privMsg(where, "But she hears only whispers of some quiet conversation")
		time.Sleep(9 * time.Second)
		privMsg(where, "She's coming in the 12:30 flight")
		time.Sleep(3 * time.Second)
		privMsg(where, "The moonlit wings reflect the stars that guide me towards salvation")
	case "wagner":
		sendToMinion(formatPlayTuneCommand("wagner.ogg", duration))
	case "nuke":
		sendToMinion(formatPlayTuneCommand("nuke_ready.ogg", duration))
	case "energy":
		sendToMinion(formatPlayTuneCommand("energy.ogg", duration))
	}
}

func (module SoundBoardModule) PrivMsg(nick, where, msg string, isAction bool) {
	if msg = AddressedToMe(msg); msg == "" {
		return
	}

	tune, duration := getTune(msg)
	if tune == "" {
		return
	}

	playTune(where, tune, duration)
}

func (module SoundBoardModule) Init() {
}
<|endoftext|>


<commit_before>/*
 * reporter-cli/sql-pdf/go/src/reporter_controller.go
 * ============================================================================
 * Reporter Multilang. Version 0.1
 * ============================================================================
 * A tool to generate human-readable reports based on data from various sources
 * with the focus on its implementation using a series of programming languages
 * ============================================================================
 * Written by Radislav (Radicchio) Golubtsov, 2016-2017
 *
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import "database/sql"
import "reflect"
import "fmt"
import "strconv"
import "time"
import "strings"

/**
 * Constant: The start date to retrieve data set.
 *     TODO: Move to cli args.
 */
const FROM string = "2016-06-01"

/**
 * Constant: The end   date to retrieve data set.
 *     TODO: Move to cli args.
 */
const TO   string = "2016-12-01"

/**
 * Constant: The PDF report output location.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_DIR string = "lib/data"

/**
 * Constant: The PDF report filename.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_FILENAME string = "packages.pdf"

/* Various string literals. */
const _ARCH_HEADER          string = "Arch"
const _REPO_HEADER          string = "Repo"
const _NAME_HEADER          string = "Name"
const _VERSION_HEADER       string = "Version"
const _LAST_UPDATED_HEADER  string = "Last Updated"
const _FLAG_DATE_HEADER     string = "Flag Date"
// ----------------------------------------------------------------------------
const _ROWS_IN_SET_FOOTER   string = " rows in set"
const _ROWS_SHOWN_FOOTER_1  string = "  ("
const _ROWS_SHOWN_FOOTER_2  string = " rows shown)"
const _PDF_REPORT_SAVED_MSG string = "PDF report saved"

/** The controller class of the application. */
type ReporterController struct {
    /*
     * Attached methods:
     *   - pdf_report_generate(cnx      *sql.DB,
     *                         postgres  bool,
     *                         exec      string) int
     *
     *   - _page_body_draw(report       string,
     *                     hdr_set  [  ]string,
     *                     row_set  [][]string,
     *                     num_hdrs     uint,
     *                     num_rows     uint) int
     *
     *   - _get_pdf_report_path(exec string) string
     */
}

/**
 * Generates the PDF report.
 *
 * @param cnx      The database connection object.
 * @param postgres The indicator that shows whether the database connection
 *                 object is PostgreSQL connection.
 * @param exec     The executable path.
 *
 * @return The exit code indicating the status of generating the PDF report.
 */
func (ReporterController) pdf_report_generate(cnx      *sql.DB,
                                              postgres  bool,
                                              exec      string) int {

    var ret int = _EXIT_SUCCESS

    var class___ ReporterController
    var __name__ string = reflect.TypeOf(class___).Name()

//  __file__ := exec

    // Instantiating the model class.
    model := new(ReporterModel)

    // Retrieving a list of all data items stored in the database.
//  hdr_set, row_set := model.get_all_data_items(cnx)

    // Retrieving a list of data items for a given date period.
    hdr_set, row_set := model.get_data_items_by_date(FROM, TO, cnx, postgres)

    num_hdrs := uint(len(hdr_set))
    num_rows := uint(len(row_set))

    // In case of getting an empty result set, informing the user.
    if (num_hdrs == 0) {
        ret = _EXIT_FAILURE

        fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                                      _COLON_SPACE_SEP + _ERROR_NO_DATA +
                                                         _NEW_LINE)

        return ret
    }

    // ------------------------------------------------------------------------
    // --- Debug output - Begin -----------------------------------------------
    // ------------------------------------------------------------------------
    dbg_output := new(TabularDisplay)

    fmt.Printf(_S_FMT, dbg_output.render(hdr_set, row_set))

    fmt.Printf(_S_FMT, strconv.Itoa(int(num_rows)) + _ROWS_IN_SET_FOOTER +
                                         _NEW_LINE + _NEW_LINE)
    // ------------------------------------------------------------------------
    // --- Debug output - End -------------------------------------------------
    // ------------------------------------------------------------------------

                            //     Waiting one second...
    time.Sleep(time.Second) // <== Just for fun...:-)...
                            //     Please!   --   OK.

    // ------------------------------------------------------------------------
    // --- Generating the PDF report - Begin ----------------------------------
    // ------------------------------------------------------------------------
//  pdf_report_path := class___._get_pdf_report_path(__file__)

    // TODO: Implement generating the PDF report.

    // ------------------------------------------------------------------------
    // --- Generating the PDF report - End ------------------------------------
    // ------------------------------------------------------------------------

    return ret
}

/* Draws the PDF report page body (data). */
func (ReporterController) _page_body_draw(report       string,
                                          hdr_set  [  ]string,
                                          row_set  [][]string,
                                          num_hdrs     uint,
                                          num_rows     uint) int {

    var ret int = _EXIT_SUCCESS

    // TODO: Implement drawing the PDF report page body.

    return ret
}

/*
 * Helper method.
 * Returns the generated PDF report output path,
 * relative to the executable's location.
 * TODO: Remove this method when the report output location
 *       will be passed through cli args.
 */
func (ReporterController) _get_pdf_report_path(exec string) string {
    exec_path := strings.Split(exec, _SLASH)

    var exec_path_len uint = uint(len(exec_path))

//  for i := uint(0); i < exec_path_len; i++ {
//      fmt.Printf(_S_FMT, exec_path[i] + _NEW_LINE)
//  }

    exec_path_new := make([]string, exec_path_len - 1)
    copy(exec_path_new, exec_path)
    exec_path_new[exec_path_len - 2] = PDF_REPORT_DIR

//  for i := uint(0); i < (exec_path_len - 1); i++ {
//      fmt.Printf(_S_FMT, exec_path_new[i] + _NEW_LINE)
//  }

    pdf_report_path := strings.Join(exec_path_new, _SLASH) + _SLASH +
                                                PDF_REPORT_FILENAME

    return pdf_report_path
}

// vim:set nu:et:ts=4:sw=4:
<commit_msg>SQL-PDF-Go: Create empty one-page PDF report using Kurt Jung's gofpdf Go package.<commit_after>/*
 * reporter-cli/sql-pdf/go/src/reporter_controller.go
 * ============================================================================
 * Reporter Multilang. Version 0.1
 * ============================================================================
 * A tool to generate human-readable reports based on data from various sources
 * with the focus on its implementation using a series of programming languages
 * ============================================================================
 * Written by Radislav (Radicchio) Golubtsov, 2016-2017
 *
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import "database/sql"
import "reflect"
import "fmt"
import "strconv"
import "time"
import "strings"
import "github.com/jung-kurt/gofpdf"

/**
 * Constant: The start date to retrieve data set.
 *     TODO: Move to cli args.
 */
const FROM string = "2016-06-01"

/**
 * Constant: The end   date to retrieve data set.
 *     TODO: Move to cli args.
 */
const TO   string = "2016-12-01"

/**
 * Constant: The PDF report output location.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_DIR string = "lib/data"

/**
 * Constant: The PDF report filename.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_FILENAME string = "packages.pdf"

/** Constant: The PDF basic measurement unit -- PostScript point. */
const PT uint    =   1

/** Constant: The one inch       (in PDF measurement terms). */
const IN float64 = ( 1   / 72)

/** Constant: The one millimeter (in PDF measurement terms). */
const MM float64 = (25.4 / 72)

/* Various string literals. */
const _REPORT_ORIENTATION_P string = "Portrait"
const _REPORT_UNITS_MM      string = "mm"
const _REPORT_PAGE_SIZE_A4  string = "A4"
// ----------------------------------------------------------------------------
const _ARCH_HEADER          string = "Arch"
const _REPO_HEADER          string = "Repo"
const _NAME_HEADER          string = "Name"
const _VERSION_HEADER       string = "Version"
const _LAST_UPDATED_HEADER  string = "Last Updated"
const _FLAG_DATE_HEADER     string = "Flag Date"
// ----------------------------------------------------------------------------
const _ROWS_IN_SET_FOOTER   string = " rows in set"
const _ROWS_SHOWN_FOOTER_1  string = "  ("
const _ROWS_SHOWN_FOOTER_2  string = " rows shown)"
const _PDF_REPORT_SAVED_MSG string = "PDF report saved"

/** The controller class of the application. */
type ReporterController struct {
    /*
     * Attached methods:
     *   - pdf_report_generate(cnx      *sql.DB,
     *                         postgres  bool,
     *                         exec      string) int
     *
     *   - _page_body_draw(report      *gofpdf.Fpdf,
     *                     hdr_set  [  ]string,
     *                     row_set  [][]string,
     *                     num_hdrs     uint,
     *                     num_rows     uint) int
     *
     *   - _get_pdf_report_path(exec string) string
     */
}

/**
 * Generates the PDF report.
 *
 * @param cnx      The database connection object.
 * @param postgres The indicator that shows whether the database connection
 *                 object is PostgreSQL connection.
 * @param exec     The executable path.
 *
 * @return The exit code indicating the status of generating the PDF report.
 */
func (ReporterController) pdf_report_generate(cnx      *sql.DB,
                                              postgres  bool,
                                              exec      string) int {

    var ret int = _EXIT_SUCCESS

    var class___ ReporterController
    var __name__ string = reflect.TypeOf(class___).Name()

    __file__ := exec

    var e error

    // Instantiating the model class.
    model := new(ReporterModel)

    // Retrieving a list of all data items stored in the database.
//  hdr_set, row_set := model.get_all_data_items(cnx)

    // Retrieving a list of data items for a given date period.
    hdr_set, row_set := model.get_data_items_by_date(FROM, TO, cnx, postgres)

    num_hdrs := uint(len(hdr_set))
    num_rows := uint(len(row_set))

    // In case of getting an empty result set, informing the user.
    if (num_hdrs == 0) {
        ret = _EXIT_FAILURE

        fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                                      _COLON_SPACE_SEP + _ERROR_NO_DATA +
                                                         _NEW_LINE)

        return ret
    }

    // ------------------------------------------------------------------------
    // --- Debug output - Begin -----------------------------------------------
    // ------------------------------------------------------------------------
    dbg_output := new(TabularDisplay)

    fmt.Printf(_S_FMT, dbg_output.render(hdr_set, row_set))

    fmt.Printf(_S_FMT, strconv.Itoa(int(num_rows)) + _ROWS_IN_SET_FOOTER +
                                         _NEW_LINE + _NEW_LINE)
    // ------------------------------------------------------------------------
    // --- Debug output - End -------------------------------------------------
    // ------------------------------------------------------------------------

                            //     Waiting one second...
    time.Sleep(time.Second) // <== Just for fun...:-)...
                            //     Please!   --   OK.

    // ------------------------------------------------------------------------
    // --- Generating the PDF report - Begin ----------------------------------
    // ------------------------------------------------------------------------
    pdf_report_path := class___._get_pdf_report_path(__file__)

    report := gofpdf.New(_REPORT_ORIENTATION_P, // <== "Portrait".
                         _REPORT_UNITS_MM,
                         _REPORT_PAGE_SIZE_A4,  // <== 210 x 297 mm.
                         _EMPTY_STRING)

    // --- Page body (data) ---------------------------------------------------
    ret=class___._page_body_draw(report, hdr_set, row_set, num_hdrs, num_rows)

    // Trying to save the report.
    e = report.OutputFileAndClose(pdf_report_path)

    if (e != nil) {
        ret = _EXIT_FAILURE

        fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                                      _COLON_SPACE_SEP + e.Error() + _NEW_LINE)

        return ret
    }

    fmt.Printf(_S_FMT, _PDF_REPORT_SAVED_MSG + _COLON_SPACE_SEP +
                        pdf_report_path      + _NEW_LINE)
    // ------------------------------------------------------------------------
    // --- Generating the PDF report - End ------------------------------------
    // ------------------------------------------------------------------------

    return ret
}

/* Draws the PDF report page body (data). */
func (ReporterController) _page_body_draw(report      *gofpdf.Fpdf,
                                          hdr_set  [  ]string,
                                          row_set  [][]string,
                                          num_hdrs     uint,
                                          num_rows     uint) int {

    var ret int = _EXIT_SUCCESS

    // TODO: Implement drawing the PDF report page body.

    return ret
}

/*
 * Helper method.
 * Returns the generated PDF report output path,
 * relative to the executable's location.
 * TODO: Remove this method when the report output location
 *       will be passed through cli args.
 */
func (ReporterController) _get_pdf_report_path(exec string) string {
    exec_path := strings.Split(exec, _SLASH)

    var exec_path_len uint = uint(len(exec_path))

//  for i := uint(0); i < exec_path_len; i++ {
//      fmt.Printf(_S_FMT, exec_path[i] + _NEW_LINE)
//  }

    exec_path_new := make([]string, exec_path_len - 1)
    copy(exec_path_new, exec_path)
    exec_path_new[exec_path_len - 2] = PDF_REPORT_DIR

//  for i := uint(0); i < (exec_path_len - 1); i++ {
//      fmt.Printf(_S_FMT, exec_path_new[i] + _NEW_LINE)
//  }

    pdf_report_path := strings.Join(exec_path_new, _SLASH) + _SLASH +
                                                PDF_REPORT_FILENAME

    return pdf_report_path
}

// vim:set nu:et:ts=4:sw=4:
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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 server

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/service-catalog/contrib/pkg/broker/controller"
	"github.com/kubernetes-incubator/service-catalog/pkg/brokerapi"
	"github.com/kubernetes-incubator/service-catalog/pkg/util"

	"github.com/gorilla/mux"
)

type server struct {
	controller controller.Controller
}

// CreateHandler creates Broker HTTP handler based on an implementation
// of a controller.Controller interface.
func CreateHandler(c controller.Controller) http.Handler {
	s := server{
		controller: c,
	}

	var router = mux.NewRouter()

	router.HandleFunc("/v2/catalog", s.catalog).Methods("GET")
	router.HandleFunc("/v2/service_instances/{instance_id}", s.getServiceInstance).Methods("GET")
	router.HandleFunc("/v2/service_instances/{instance_id}", s.createServiceInstance).Methods("PUT")
	router.HandleFunc("/v2/service_instances/{instance_id}", s.removeServiceInstance).Methods("DELETE")
	router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", s.bind).Methods("PUT")
	router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", s.unBind).Methods("DELETE")

	return router
}

// Start creates the HTTP handler based on an implementation of a
// controller.Controller interface, and begins to listen on the specified port.
func Start(serverPort int, c controller.Controller) {
	glog.Infof("Starting server on %d\n", serverPort)
	http.Handle("/", CreateHandler(c))
	if err := http.ListenAndServe(":"+strconv.Itoa(serverPort), nil); err != nil {
		panic(err)
	}
}

func (s *server) catalog(w http.ResponseWriter, r *http.Request) {
	glog.Infof("Get Service Broker Catalog...")

	if result, err := s.controller.Catalog(); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) getServiceInstance(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["instance_id"]
	glog.Infof("GetServiceInstance ... %s\n", id)

	if result, err := s.controller.GetServiceInstance(id); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) createServiceInstance(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["instance_id"]
	glog.Infof("CreateServiceInstance %s...\n", id)

	var req brokerapi.CreateServiceInstanceRequest
	if err := util.BodyToObject(r, &req); err != nil {
		glog.Errorf("error unmarshalling: %v", err)
		util.WriteResponse(w, http.StatusBadRequest, err)
		return
	}

	// TODO: Check if parameters are required, if not, this thing below is ok to leave in,
	// if they are ,they should be checked. Because if no parameters are passed in, this will
	// fail because req.Parameters is nil.
	if req.Parameters == nil {
		req.Parameters = make(map[string]interface{})
	}

	if result, err := s.controller.CreateServiceInstance(id, &req); err == nil {
		util.WriteResponse(w, http.StatusCreated, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) removeServiceInstance(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["instance_id"]
	glog.Infof("RemoveServiceInstance %s...\n", id)

	if result, err := s.controller.RemoveServiceInstance(id); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) bind(w http.ResponseWriter, r *http.Request) {
	bindingID := mux.Vars(r)["binding_id"]
	instanceID := mux.Vars(r)["instance_id"]

	glog.Infof("Bind binding_id=%s, instance_id=%s\n", bindingID, instanceID)

	var req brokerapi.BindingRequest

	if err := util.BodyToObject(r, &req); err != nil {
		glog.Errorf("Failed to unmarshall request: %v", err)
		util.WriteResponse(w, http.StatusBadRequest, err)
		return
	}

	// TODO: Check if parameters are required, if not, this thing below is ok to leave in,
	// if they are ,they should be checked. Because if no parameters are passed in, this will
	// fail because req.Parameters is nil.
	if req.Parameters == nil {
		req.Parameters = make(map[string]interface{})
	}

	// Pass in the instanceId to the template.
	req.Parameters["instanceId"] = instanceID

	if result, err := s.controller.Bind(instanceID, bindingID, &req); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) unBind(w http.ResponseWriter, r *http.Request) {
	instanceID := mux.Vars(r)["instance_id"]
	bindingID := mux.Vars(r)["binding_id"]
	glog.Infof("UnBind: Service instance guid: %s:%s", bindingID, instanceID)

	if err := s.controller.UnBind(instanceID, bindingID); err == nil {
		w.WriteHeader(http.StatusOK)
		fmt.Print(w, "{}") //id)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}
<commit_msg>Make ups-broker return valid unbind response (#964)<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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 server

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/service-catalog/contrib/pkg/broker/controller"
	"github.com/kubernetes-incubator/service-catalog/pkg/brokerapi"
	"github.com/kubernetes-incubator/service-catalog/pkg/util"

	"github.com/gorilla/mux"
)

type server struct {
	controller controller.Controller
}

// CreateHandler creates Broker HTTP handler based on an implementation
// of a controller.Controller interface.
func CreateHandler(c controller.Controller) http.Handler {
	s := server{
		controller: c,
	}

	var router = mux.NewRouter()

	router.HandleFunc("/v2/catalog", s.catalog).Methods("GET")
	router.HandleFunc("/v2/service_instances/{instance_id}", s.getServiceInstance).Methods("GET")
	router.HandleFunc("/v2/service_instances/{instance_id}", s.createServiceInstance).Methods("PUT")
	router.HandleFunc("/v2/service_instances/{instance_id}", s.removeServiceInstance).Methods("DELETE")
	router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", s.bind).Methods("PUT")
	router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", s.unBind).Methods("DELETE")

	return router
}

// Start creates the HTTP handler based on an implementation of a
// controller.Controller interface, and begins to listen on the specified port.
func Start(serverPort int, c controller.Controller) {
	glog.Infof("Starting server on %d\n", serverPort)
	http.Handle("/", CreateHandler(c))
	if err := http.ListenAndServe(":"+strconv.Itoa(serverPort), nil); err != nil {
		panic(err)
	}
}

func (s *server) catalog(w http.ResponseWriter, r *http.Request) {
	glog.Infof("Get Service Broker Catalog...")

	if result, err := s.controller.Catalog(); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) getServiceInstance(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["instance_id"]
	glog.Infof("GetServiceInstance ... %s\n", id)

	if result, err := s.controller.GetServiceInstance(id); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) createServiceInstance(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["instance_id"]
	glog.Infof("CreateServiceInstance %s...\n", id)

	var req brokerapi.CreateServiceInstanceRequest
	if err := util.BodyToObject(r, &req); err != nil {
		glog.Errorf("error unmarshalling: %v", err)
		util.WriteResponse(w, http.StatusBadRequest, err)
		return
	}

	// TODO: Check if parameters are required, if not, this thing below is ok to leave in,
	// if they are ,they should be checked. Because if no parameters are passed in, this will
	// fail because req.Parameters is nil.
	if req.Parameters == nil {
		req.Parameters = make(map[string]interface{})
	}

	if result, err := s.controller.CreateServiceInstance(id, &req); err == nil {
		util.WriteResponse(w, http.StatusCreated, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) removeServiceInstance(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["instance_id"]
	glog.Infof("RemoveServiceInstance %s...\n", id)

	if result, err := s.controller.RemoveServiceInstance(id); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) bind(w http.ResponseWriter, r *http.Request) {
	bindingID := mux.Vars(r)["binding_id"]
	instanceID := mux.Vars(r)["instance_id"]

	glog.Infof("Bind binding_id=%s, instance_id=%s\n", bindingID, instanceID)

	var req brokerapi.BindingRequest

	if err := util.BodyToObject(r, &req); err != nil {
		glog.Errorf("Failed to unmarshall request: %v", err)
		util.WriteResponse(w, http.StatusBadRequest, err)
		return
	}

	// TODO: Check if parameters are required, if not, this thing below is ok to leave in,
	// if they are ,they should be checked. Because if no parameters are passed in, this will
	// fail because req.Parameters is nil.
	if req.Parameters == nil {
		req.Parameters = make(map[string]interface{})
	}

	// Pass in the instanceId to the template.
	req.Parameters["instanceId"] = instanceID

	if result, err := s.controller.Bind(instanceID, bindingID, &req); err == nil {
		util.WriteResponse(w, http.StatusOK, result)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}

func (s *server) unBind(w http.ResponseWriter, r *http.Request) {
	instanceID := mux.Vars(r)["instance_id"]
	bindingID := mux.Vars(r)["binding_id"]
	glog.Infof("UnBind: Service instance guid: %s:%s", bindingID, instanceID)

	if err := s.controller.UnBind(instanceID, bindingID); err == nil {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprint(w, "{}") //id)
	} else {
		util.WriteResponse(w, http.StatusBadRequest, err)
	}
}
<|endoftext|>