<commit_before>package rabric

import (
	"fmt"
	"sync"
	"time"
)

type node struct {
	closing               bool
	closeLock             sync.Mutex
	sessionOpenCallbacks  []func(uint, string)
	sessionCloseCallbacks []func(uint, string)
	realm                 Realm
	sessionPdid           map[string]string
	nodes                 map[string]Session
	forwarding            map[string]Session
	permissions           map[string]string
	agent                 *Client
}

// NewDefaultRouter creates a very basic WAMP router.
func NewNode() Router {
	node := &node{
		sessionOpenCallbacks:  []func(uint, string){},
		sessionCloseCallbacks: []func(uint, string){},
	}

	// Provisioning: this router needs a name
	// Unhandled case: what to do with routers that start with nothing?
	// They still have to create a peer (self) and ask for an identity

	// Here we assume *one* router as root and a default pd namespace
	// Router should identify itself based on its root certificate, for now
	// just set a constant name
	realm := Realm{URI: "pd"}
	realm.init()

	// node.realms["pd"] = realm

	// Experimental single realm testing--- since we're handling the
	// pubs and subs to begin with
	node.realm = realm

	// peer, ok := node.GetLocalPeer("pd", nil)

	// if peer, ok := node.GetLocalPeer("pd", nil); ok != nil {
	//     //log.Println("Unable to create local session: ", ok)
	// } else {
	//     node.agent = peer
	// }

	node.agent = node.localClient("pd")

	// Subscribe to meta-level events here
	// TODO: create new object to handle this, no inline
	// h := func(args []interface{}, kwargs map[string]interface{}) {
	// 	out.Warning("Got a pub on the local session!")
	// }

	// node.agent.Subscribe("pd/hello", h)

	return node
}

func (n *node) localClient(s string) *Client {
	p := n.getTestPeer()

	// p, _ := n.GetLocalPeer(URI(s), nil)

	client := NewClient(p)
	client.ReceiveTimeout = 100 * time.Millisecond
	if _, err := client.JoinRealm(s, nil); err != nil {
		out.Error("Error when creating new client: ", err)
	}

	return client
}

func (r *node) AddSessionOpenCallback(fn func(uint, string)) {
	r.sessionOpenCallbacks = append(r.sessionOpenCallbacks, fn)
}

func (r *node) AddSessionCloseCallback(fn func(uint, string)) {
	r.sessionCloseCallbacks = append(r.sessionCloseCallbacks, fn)
}

func (r *node) Close() error {
	r.closeLock.Lock()

	if r.closing {
		r.closeLock.Unlock()
		return fmt.Errorf("already closed")
	}

	r.closing = true
	r.closeLock.Unlock()
	r.realm.Close()

	return nil
}

// Shouldn't be called anymore
func (r *node) RegisterRealm(uri URI, realm Realm) error {
	return nil
}

func (r *node) Accept(client Peer) error {
	sess, ok := r.Handshake(client)

	if ok != nil {
		return ok
	}

	// sess := Session{Peer: client, Id: welcome.Id, pdid: hello.Realm, kill: make(chan URI, 1)}
	// out.Notice("Established session: ", sess.pdid)

	// Meta level start events
	// for _, callback := range r.sessionOpenCallbacks {
	// 	go callback(uint(sess.Id), string(hello.Realm))
	// }

	// OLD CODE: need the original realm to handle issues with default
	// realm := r.getDomain(sess.pdid)

	// Start listening on the session
	// This will eventually move to the session
	go Listen(r, sess)

	return nil
}

////////////////////////////////////////
// New Content
////////////////////////////////////////

// Spin on a session, wait for messages to arrive. Method does not return
// until session closes
// NOTE: realm and details are OLD CODE and should not be construed as permanent fixtures
func Listen(node *node, sess Session) {
	c := sess.Receive()

	for {
		var open bool
		var msg Message

		select {
		case msg, open = <-c:
			if !open {
				//log.Println("lost session:", sess)

				node.SessionClose(sess)
				return
			}

		case reason := <-sess.kill:
			logErr(sess.Send(&Goodbye{Reason: reason, Details: make(map[string]interface{})}))
			//log.Printf("kill session %s: %v", sess, reason)

			//NEW: Exit the session!
			node.SessionClose(sess)
			return
		}

		node.Handle(&msg, &sess)
	}
}

////////////////////////////////////////
// Very new code
////////////////////////////////////////

// Handle a new Peer, creating and returning a session
func (n *node) Handshake(client Peer) (Session, error) {
	sess := Session{}

	// Dont accept new sessions if the node is going down
	if n.closing {
		logErr(client.Send(&Abort{Reason: ErrSystemShutdown}))
		logErr(client.Close())
		return sess, fmt.Errorf("Router is closing, no new connections are allowed")
	}

	msg, err := GetMessageTimeout(client, 5*time.Second)
	if err != nil {
		return sess, err
	}

	hello, msgOk := msg.(*Hello)

	// Ensure the message is valid and well constructed
	if !msgOk {
		logErr(client.Send(&Abort{Reason: URI("wamp.error.protocol_violation")}))
		logErr(client.Close())

		return sess, fmt.Errorf("protocol violation: expected HELLO, received %s", msg.MessageType())
	}

	// get the appropriate domain
	// realm := n.getDomain(hello.Realm)
	// realm :=

	// Old implementation: the authentication must occur before fetching the realm
	welcome, err := n.realm.handleAuth(client, hello.Details)

	if err != nil {
		abort := &Abort{
			Reason:  ErrAuthorizationFailed, // TODO: should this be AuthenticationFailed?
			Details: map[string]interface{}{"error": err.Error()},
		}

		logErr(client.Send(abort))
		logErr(client.Close())
		return sess, AuthenticationError(err.Error())
	}

	welcome.Id = NewID()

	if welcome.Details == nil {
		welcome.Details = make(map[string]interface{})
	}

	// add default details to welcome message
	for k, v := range defaultWelcomeDetails {
		if _, ok := welcome.Details[k]; !ok {
			welcome.Details[k] = v
		}
	}

	if err := client.Send(welcome); err != nil {
		return sess, err
	}

	out.Notice("Handshake session: ", hello.Realm)

	sess = Session{Peer: client, Id: welcome.Id, pdid: hello.Realm, kill: make(chan URI, 1)}
	return sess, nil
}

// Called when a session is closed or closes itself
func (n *node) SessionClose(sess Session) {
	sess.Close()
	out.Info("Session %s closed", sess)

	// Did these really not exist before? Doesn't seem likely, but can't find them
	n.realm.Dealer.lostSession(sess)
	n.realm.Broker.lostSession(sess)

	// Meta level events
	// for _, callback := range n.sessionCloseCallbacks {
	//     go callback(uint(sess.Id), string(hello.Realm))
	// }
}

// Handle a new message
func (n *node) Handle(msg *Message, sess *Session) {
	// NOTE: there is a serious shortcoming here: How do we deal with WAMP messages with an
	// implicit destination? Many of them refer to sessions, but do we want to store the session
	// IDs with the ultimate PDID target, or just change the protocol?

	m := *msg
	out.Debug("[%s] %s: %+v", *sess, m.MessageType(), *msg)

	if uri, ok := destination(msg); ok == nil {
		// Ensure the construction of the message is valid, extract the endpoint, domain, and action
		// domain, action, err := breakdownEndpoint(string(uri))
		_, action, err := breakdownEndpoint(string(uri))

		// Return a WAMP error to the user indicating a poorly constructed endpoint
		if err != nil {
			out.Error("Misconstructed endpoint: %s", msg)
			m := *msg
			err := &Error{
				Type:    m.MessageType(),
				Request: sess.Id,
				Details: map[string]interface{}{"Invalid Endpoint": "Poorly constructed endpoint."},
				Error:   ErrInvalidUri,
			}
			sess.Peer.Send(err)

			return
		}

		// out.Debug("Extracted: %s %s \n", domain, action)

		if !n.Permitted(msg, sess) {
			out.Error("Operation not permitted! TODO: return an error here!")
			return
		}

		// Testing
		if action == "/ping" {
			// out.Critical("Trying session lookup...")

			// Try and check if the given endpoint is registered.

			// For now, dump the realm
			s := n.realm.dump()
			out.Critical(s)

			exists := n.realm.hasSubscription("pd/pong")
			out.Critical("Subscription for pd/ping exists: ", exists)

			if exists {
				out.Critical("Sending blind pub on pd/pong")

				n.agent.Publish("pd/pong", nil, nil)
			}
		}

		// Delivery (deferred)
		// route = n.Route(msg)

		// n.CoreReady()

	} else {
		// //log.Printf("Unable to determine destination from message: %+v", *msg)
		out.Debug("Unable to determine destination from message: %+v", *msg)
		// n.realm.handleMessage(*msg, *sess)
	}

	n.realm.handleMessage(*msg, *sess)
}

// Return true or false based on the message and the session which sent the messate
func (n *node) Permitted(msg *Message, sess *Session) bool {
	// Is downward action? allow
	// Check permissions cache: if found, allow
	// Check with bouncer
	return true
}

// returns the pdid of the next hop on the path for the given message
func (n *node) Route(msg *Message) string {
	// Is target a tenant?
	// Is target in forwarding tables?
	// Ask map for next hop

	return ""
}

// Returns true if core appliances connected
func (n *node) CoreReady() bool {
	out.Warning("Core status: ", n.realm)
	return true
}

////////////////////////////////////////
// Old code, not sure if still useful
////////////////////////////////////////

// GetLocalPeer returns an internal peer connected to the specified realm.
func (r *node) GetLocalPeer(realmURI URI, details map[string]interface{}) (Peer, error) {
	peerA, peerB := localPipe()
	sess := Session{Peer: peerA, Id: NewID(), kill: make(chan URI, 1)}
	out.Notice("Established internal session:", sess.Id)

	// TODO: session open/close callbacks?
	if details == nil {
		details = make(map[string]interface{})
	}

	go r.realm.handleSession(sess, details)
	return peerB, nil
}

func (r *node) getTestPeer() Peer {
	peerA, peerB := localPipe()
	go r.Accept(peerA)
	return peerB
}
<commit_msg>same old<commit_after>package rabric

import (
	"fmt"
	"sync"
	"time"
)

type node struct {
	closing               bool
	closeLock             sync.Mutex
	sessionOpenCallbacks  []func(uint, string)
	sessionCloseCallbacks []func(uint, string)
	realm                 Realm
	sessionPdid           map[string]string
	nodes                 map[string]Session
	forwarding            map[string]Session
	permissions           map[string]string
	agent                 *Client
}

// NewDefaultRouter creates a very basic WAMP router.
func NewNode() Router {
	node := &node{
		sessionOpenCallbacks:  []func(uint, string){},
		sessionCloseCallbacks: []func(uint, string){},
	}

	// Provisioning: this router needs a name
	// Unhandled case: what to do with routers that start with nothing?
	// They still have to create a peer (self) and ask for an identity

	// Here we assume *one* router as root and a default pd namespace
	// Router should identify itself based on its root certificate, for now
	// just set a constant name
	realm := Realm{URI: "pd"}
	realm.init()

	// node.realms["pd"] = realm

	// Experimental single realm testing--- since we're handling the
	// pubs and subs to begin with
	node.realm = realm

	// peer, ok := node.GetLocalPeer("pd", nil)

	// if peer, ok := node.GetLocalPeer("pd", nil); ok != nil {
	//     //log.Println("Unable to create local session: ", ok)
	// } else {
	//     node.agent = peer
	// }

	node.agent = node.localClient("pd")

	// Subscribe to meta-level events here
	// TODO: create new object to handle this, no inline
	// h := func(args []interface{}, kwargs map[string]interface{}) {
	// 	out.Warning("Got a pub on the local session!")
	// }

	// node.agent.Subscribe("pd/hello", h)

	return node
}

func (n *node) localClient(s string) *Client {
	p := n.getTestPeer()

	// p, _ := n.GetLocalPeer(URI(s), nil)

	client := NewClient(p)
	client.ReceiveTimeout = 100 * time.Millisecond
	if _, err := client.JoinRealm(s, nil); err != nil {
		out.Error("Error when creating new client: ", err)
	}

	return client
}

func (r *node) AddSessionOpenCallback(fn func(uint, string)) {
	r.sessionOpenCallbacks = append(r.sessionOpenCallbacks, fn)
}

func (r *node) AddSessionCloseCallback(fn func(uint, string)) {
	r.sessionCloseCallbacks = append(r.sessionCloseCallbacks, fn)
}

func (r *node) Close() error {
	r.closeLock.Lock()

	if r.closing {
		r.closeLock.Unlock()
		return fmt.Errorf("already closed")
	}

	r.closing = true
	r.closeLock.Unlock()
	r.realm.Close()

	return nil
}

// Shouldn't be called anymore
func (r *node) RegisterRealm(uri URI, realm Realm) error {
	return nil
}

func (r *node) Accept(client Peer) error {
	sess, ok := r.Handshake(client)

	if ok != nil {
		return ok
	}

	// sess := Session{Peer: client, Id: welcome.Id, pdid: hello.Realm, kill: make(chan URI, 1)}
	// out.Notice("Established session: ", sess.pdid)

	// Meta level start events
	// for _, callback := range r.sessionOpenCallbacks {
	// 	go callback(uint(sess.Id), string(hello.Realm))
	// }

	// OLD CODE: need the original realm to handle issues with default
	// realm := r.getDomain(sess.pdid)

	// Start listening on the session
	// This will eventually move to the session
	go Listen(r, sess)

	return nil
}

////////////////////////////////////////
// New Content
////////////////////////////////////////

// Spin on a session, wait for messages to arrive. Method does not return
// until session closes
// NOTE: realm and details are OLD CODE and should not be construed as permanent fixtures
func Listen(node *node, sess Session) {
	c := sess.Receive()

	for {
		var open bool
		var msg Message

		select {
		case msg, open = <-c:
			if !open {
				//log.Println("lost session:", sess)

				node.SessionClose(sess)
				return
			}

		case reason := <-sess.kill:
			logErr(sess.Send(&Goodbye{Reason: reason, Details: make(map[string]interface{})}))
			//log.Printf("kill session %s: %v", sess, reason)

			//NEW: Exit the session!
			node.SessionClose(sess)
			return
		}

		node.Handle(&msg, &sess)
	}
}

////////////////////////////////////////
// Very new code
////////////////////////////////////////

// Handle a new Peer, creating and returning a session
func (n *node) Handshake(client Peer) (Session, error) {
	sess := Session{}

	// Dont accept new sessions if the node is going down
	if n.closing {
		logErr(client.Send(&Abort{Reason: ErrSystemShutdown}))
		logErr(client.Close())
		return sess, fmt.Errorf("Router is closing, no new connections are allowed")
	}

	msg, err := GetMessageTimeout(client, 5*time.Second)
	if err != nil {
		return sess, err
	}

	hello, msgOk := msg.(*Hello)

	// Ensure the message is valid and well constructed
	if !msgOk {
		logErr(client.Send(&Abort{Reason: URI("wamp.error.protocol_violation")}))
		logErr(client.Close())

		return sess, fmt.Errorf("protocol violation: expected HELLO, received %s", msg.MessageType())
	}

	// get the appropriate domain
	// realm := n.getDomain(hello.Realm)
	// realm :=

	// Old implementation: the authentication must occur before fetching the realm
	welcome, err := n.realm.handleAuth(client, hello.Details)

	if err != nil {
		abort := &Abort{
			Reason:  ErrAuthorizationFailed, // TODO: should this be AuthenticationFailed?
			Details: map[string]interface{}{"error": err.Error()},
		}

		logErr(client.Send(abort))
		logErr(client.Close())
		return sess, AuthenticationError(err.Error())
	}

	welcome.Id = NewID()

	if welcome.Details == nil {
		welcome.Details = make(map[string]interface{})
	}

	// add default details to welcome message
	for k, v := range defaultWelcomeDetails {
		if _, ok := welcome.Details[k]; !ok {
			welcome.Details[k] = v
		}
	}

	if err := client.Send(welcome); err != nil {
		return sess, err
	}

	out.Notice("Handshake session: ", hello.Realm)

	sess = Session{Peer: client, Id: welcome.Id, pdid: hello.Realm, kill: make(chan URI, 1)}
	return sess, nil
}

// Called when a session is closed or closes itself
func (n *node) SessionClose(sess Session) {
	sess.Close()
	out.Notice("Session %s closed", sess)

	// Did these really not exist before? Doesn't seem likely, but can't find them
	n.realm.Dealer.lostSession(sess)
	n.realm.Broker.lostSession(sess)

	// Meta level events
	// for _, callback := range n.sessionCloseCallbacks {
	//     go callback(uint(sess.Id), string(hello.Realm))
	// }
}

// Handle a new message
func (n *node) Handle(msg *Message, sess *Session) {
	// NOTE: there is a serious shortcoming here: How do we deal with WAMP messages with an
	// implicit destination? Many of them refer to sessions, but do we want to store the session
	// IDs with the ultimate PDID target, or just change the protocol?

	m := *msg
	out.Debug("[%s] %s: %+v", *sess, m.MessageType(), *msg)

	if uri, ok := destination(msg); ok == nil {
		// Ensure the construction of the message is valid, extract the endpoint, domain, and action
		// domain, action, err := breakdownEndpoint(string(uri))
		_, action, err := breakdownEndpoint(string(uri))

		// Return a WAMP error to the user indicating a poorly constructed endpoint
		if err != nil {
			out.Error("Misconstructed endpoint: %s", msg)
			m := *msg
			err := &Error{
				Type:    m.MessageType(),
				Request: sess.Id,
				Details: map[string]interface{}{"Invalid Endpoint": "Poorly constructed endpoint."},
				Error:   ErrInvalidUri,
			}
			sess.Peer.Send(err)

			return
		}

		// out.Debug("Extracted: %s %s \n", domain, action)

		if !n.Permitted(msg, sess) {
			out.Error("Operation not permitted! TODO: return an error here!")
			return
		}

		// Testing
		if action == "/ping" {
			// out.Critical("Trying session lookup...")

			// Try and check if the given endpoint is registered.

			// For now, dump the realm
			s := n.realm.dump()
			out.Critical(s)

			exists := n.realm.hasSubscription("pd/pong")
			out.Critical("Subscription for pd/ping exists: ", exists)

			if exists {
				out.Critical("Sending blind pub on pd/pong")

				n.agent.Publish("pd/pong", nil, nil)
			}
		}

		// Delivery (deferred)
		// route = n.Route(msg)

		// n.CoreReady()

	} else {
		// //log.Printf("Unable to determine destination from message: %+v", *msg)
		out.Debug("Unable to determine destination from message: %+v", *msg)
		// n.realm.handleMessage(*msg, *sess)
	}

	n.realm.handleMessage(*msg, *sess)
}

// Return true or false based on the message and the session which sent the messate
func (n *node) Permitted(msg *Message, sess *Session) bool {
	// Is downward action? allow
	// Check permissions cache: if found, allow
	// Check with bouncer
	return true
}

// returns the pdid of the next hop on the path for the given message
func (n *node) Route(msg *Message) string {
	// Is target a tenant?
	// Is target in forwarding tables?
	// Ask map for next hop

	return ""
}

// Returns true if core appliances connected
func (n *node) CoreReady() bool {
	out.Warning("Core status: ", n.realm)
	return true
}

////////////////////////////////////////
// Old code, not sure if still useful
////////////////////////////////////////

// GetLocalPeer returns an internal peer connected to the specified realm.
func (r *node) GetLocalPeer(realmURI URI, details map[string]interface{}) (Peer, error) {
	peerA, peerB := localPipe()
	sess := Session{Peer: peerA, Id: NewID(), kill: make(chan URI, 1)}
	out.Notice("Established internal session:", sess.Id)

	// TODO: session open/close callbacks?
	if details == nil {
		details = make(map[string]interface{})
	}

	go r.realm.handleSession(sess, details)
	return peerB, nil
}

func (r *node) getTestPeer() Peer {
	peerA, peerB := localPipe()
	go r.Accept(peerA)
	return peerB
}
<|endoftext|>


<commit_before>package elasticsearch

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"
)

// A Node is a structure which represents a single ElasticSearch host.
type Node struct {
	sync.RWMutex
	endpoint   string
	health     Health
	client     *http.Client // default http client
	pingClient *http.Client // used for Ping() only
}

// NewNode constructs a Node handle. The endpoint should be of the form
// "scheme://host:port", eg. "http://es001:9200".
//
// The ping interval is dictated at a higher level (the Cluster), but individual
// ping timeouts are stored with the Nodes themselves, in a custom HTTP client,
// with a timeout as part of the Transport dialer. This custom pingClient is
// used exclusively for Ping() calls.
//
// Regular queries are made with the default client http.Client, which has
// no explicit timeout set in the Transport dialer.
func NewNode(endpoint string, pingTimeout time.Duration) *Node {
	return &Node{
		endpoint: endpoint,
		health:   Yellow,
		client: &http.Client{
			Transport: &http.Transport{
				MaxIdleConnsPerHost: 250,
			},
		},
		pingClient: &http.Client{
			Transport: &http.Transport{
				Dial: timeoutDialer(pingTimeout),
			},
		},
	}
}

// Ping attempts to HTTP GET a specific endpoint
// and returns true if everything was successful.
func (n *Node) Ping() bool {
	u, err := url.Parse(n.endpoint)
	if err != nil {
		log.Printf("ElasticSearch: ping: resolve: %s", err)
		return false
	}
	u.Path = "/" // some arbitrary, reasonable endpoint

	resp, err := n.pingClient.Get(u.String())
	if err != nil {
		log.Printf("ElasticSearch: ping %s: GET: %s", u.Host, err)
		return false
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		log.Printf("ElasticSearch: ping %s: GET response: %s", u.Host, resp.Status)
		return false
	}

	return true
}

// PingAndSet performs a Ping, and updates the Node's health accordingly.
func (n *Node) pingAndSet() {
	success := n.Ping()
	func() {
		n.Lock()
		defer n.Unlock()
		if success {
			n.health = n.health.Improve()
		} else {
			n.health = n.health.Degrade()
		}
	}()
}

// GetHealth returns the health of the node, for use in the Cluster's GetBest.
func (n *Node) GetHealth() Health {
	n.RLock()
	defer n.RUnlock()
	return n.health
}

// Executes the Fireable f against the node and decodes the server's reply into
// response.
func (n *Node) Execute(f Fireable, response interface{}) error {
	uri, err := url.Parse(n.endpoint)
	if err != nil {
		return err
	}

	request, err := f.Request(uri)
	if err != nil {
		return err
	}

	r, err := n.client.Do(request)
	if err != nil {
		return err
	}

	defer r.Body.Close()

	return json.NewDecoder(r.Body).Decode(response)
}

//
//
//

type Nodes []*Node

// PingAll triggers simultaneous PingAndSets across all Nodes,
// and blocks until they've all completed.
func (n Nodes) pingAll() {
	c := make(chan bool, len(n))
	for _, node := range n {
		go func(tgt *Node) { tgt.pingAndSet(); c <- true }(node)
	}
	for i := 0; i < cap(c); i++ {
		<-c
	}
}

// GetBest returns the "best" Node, as decided by each Node's health.
// It's possible that no Node will be healthy enough to be returned.
// In that case, GetBest returns an error, and processing cannot continue.
func (n Nodes) getBest() (*Node, error) {
	green, yellow := []*Node{}, []*Node{}
	for _, node := range n {
		switch node.GetHealth() {
		case Green:
			green = append(green, node)
		case Yellow:
			yellow = append(yellow, node)
		}
	}

	if len(green) > 0 {
		return green[rand.Intn(len(green))], nil
	}

	if len(yellow) > 0 {
		return yellow[rand.Intn(len(yellow))], nil
	}

	return nil, fmt.Errorf("no healthy nodes available")
}

//
//
//

// Health is some encoding of the perceived state of a Node.
// A Cluster should favor sending queries against healthier nodes.
type Health int

const (
	Green Health = iota // resemblance to cluster health codes is coincidental
	Yellow
	Red
)

func (h Health) String() string {
	switch h {
	case Green:
		return "Green"
	case Yellow:
		return "Yellow"
	case Red:
		return "Red"
	}
	panic("unreachable")
}

func (h Health) Improve() Health {
	switch h {
	case Red:
		return Yellow
	default:
		return Green
	}
	panic("unreachable")
}

func (h Health) Degrade() Health {
	switch h {
	case Green:
		return Yellow
	default:
		return Red
	}
	panic("unreachable")
}

//
//
//

// timeoutDialer returns a function that can be put into an HTTP Client's
// Transport, which will cause all requests made on that client to abort
// if they're not handled within the passed duration.
func timeoutDialer(d time.Duration) func(net, addr string) (net.Conn, error) {
	return func(netw, addr string) (net.Conn, error) {
		c, err := net.Dial(netw, addr)
		if err != nil {
			return nil, err
		}
		c.SetDeadline(time.Now().Add(d))
		return c, nil
	}
}
<commit_msg>Lower max number of idle search connections to cluster<commit_after>package elasticsearch

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"
)

// A Node is a structure which represents a single ElasticSearch host.
type Node struct {
	sync.RWMutex
	endpoint   string
	health     Health
	client     *http.Client // default http client
	pingClient *http.Client // used for Ping() only
}

// NewNode constructs a Node handle. The endpoint should be of the form
// "scheme://host:port", eg. "http://es001:9200".
//
// The ping interval is dictated at a higher level (the Cluster), but individual
// ping timeouts are stored with the Nodes themselves, in a custom HTTP client,
// with a timeout as part of the Transport dialer. This custom pingClient is
// used exclusively for Ping() calls.
//
// Regular queries are made with the default client http.Client, which has
// no explicit timeout set in the Transport dialer.
func NewNode(endpoint string, pingTimeout time.Duration) *Node {
	return &Node{
		endpoint: endpoint,
		health:   Yellow,
		client: &http.Client{
			Transport: &http.Transport{
				MaxIdleConnsPerHost: 50,
			},
		},
		pingClient: &http.Client{
			Transport: &http.Transport{
				Dial: timeoutDialer(pingTimeout),
			},
		},
	}
}

// Ping attempts to HTTP GET a specific endpoint
// and returns true if everything was successful.
func (n *Node) Ping() bool {
	u, err := url.Parse(n.endpoint)
	if err != nil {
		log.Printf("ElasticSearch: ping: resolve: %s", err)
		return false
	}
	u.Path = "/" // some arbitrary, reasonable endpoint

	resp, err := n.pingClient.Get(u.String())
	if err != nil {
		log.Printf("ElasticSearch: ping %s: GET: %s", u.Host, err)
		return false
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		log.Printf("ElasticSearch: ping %s: GET response: %s", u.Host, resp.Status)
		return false
	}

	return true
}

// PingAndSet performs a Ping, and updates the Node's health accordingly.
func (n *Node) pingAndSet() {
	success := n.Ping()
	func() {
		n.Lock()
		defer n.Unlock()
		if success {
			n.health = n.health.Improve()
		} else {
			n.health = n.health.Degrade()
		}
	}()
}

// GetHealth returns the health of the node, for use in the Cluster's GetBest.
func (n *Node) GetHealth() Health {
	n.RLock()
	defer n.RUnlock()
	return n.health
}

// Executes the Fireable f against the node and decodes the server's reply into
// response.
func (n *Node) Execute(f Fireable, response interface{}) error {
	uri, err := url.Parse(n.endpoint)
	if err != nil {
		return err
	}

	request, err := f.Request(uri)
	if err != nil {
		return err
	}

	r, err := n.client.Do(request)
	if err != nil {
		return err
	}

	defer r.Body.Close()

	return json.NewDecoder(r.Body).Decode(response)
}

//
//
//

type Nodes []*Node

// PingAll triggers simultaneous PingAndSets across all Nodes,
// and blocks until they've all completed.
func (n Nodes) pingAll() {
	c := make(chan bool, len(n))
	for _, node := range n {
		go func(tgt *Node) { tgt.pingAndSet(); c <- true }(node)
	}
	for i := 0; i < cap(c); i++ {
		<-c
	}
}

// GetBest returns the "best" Node, as decided by each Node's health.
// It's possible that no Node will be healthy enough to be returned.
// In that case, GetBest returns an error, and processing cannot continue.
func (n Nodes) getBest() (*Node, error) {
	green, yellow := []*Node{}, []*Node{}
	for _, node := range n {
		switch node.GetHealth() {
		case Green:
			green = append(green, node)
		case Yellow:
			yellow = append(yellow, node)
		}
	}

	if len(green) > 0 {
		return green[rand.Intn(len(green))], nil
	}

	if len(yellow) > 0 {
		return yellow[rand.Intn(len(yellow))], nil
	}

	return nil, fmt.Errorf("no healthy nodes available")
}

//
//
//

// Health is some encoding of the perceived state of a Node.
// A Cluster should favor sending queries against healthier nodes.
type Health int

const (
	Green Health = iota // resemblance to cluster health codes is coincidental
	Yellow
	Red
)

func (h Health) String() string {
	switch h {
	case Green:
		return "Green"
	case Yellow:
		return "Yellow"
	case Red:
		return "Red"
	}
	panic("unreachable")
}

func (h Health) Improve() Health {
	switch h {
	case Red:
		return Yellow
	default:
		return Green
	}
	panic("unreachable")
}

func (h Health) Degrade() Health {
	switch h {
	case Green:
		return Yellow
	default:
		return Red
	}
	panic("unreachable")
}

//
//
//

// timeoutDialer returns a function that can be put into an HTTP Client's
// Transport, which will cause all requests made on that client to abort
// if they're not handled within the passed duration.
func timeoutDialer(d time.Duration) func(net, addr string) (net.Conn, error) {
	return func(netw, addr string) (net.Conn, error) {
		c, err := net.Dial(netw, addr)
		if err != nil {
			return nil, err
		}
		c.SetDeadline(time.Now().Add(d))
		return c, nil
	}
}
<|endoftext|>


<commit_before>package dht

import (
	"fmt"
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/stvp/rendezvous"
)

const (
	defaultCheckInterval = 5 * time.Second
)

// Node is a single node in a distributed hash table that is coordinated using
// services registered in Consul. Key membership by nodes is calculated using
// rendezvous hashing to ensure even distribution and minimal key membership
// changes when a node fails or leaves.
type Node struct {
	// Consul service config
	serviceName string
	serviceID   string

	// Consul agent client
	consul *api.Client

	// Consul HTTP health check server
	checkURL      string
	checkListener net.Listener
	checkServer   *http.Server

	// Hash table
	hashMutex sync.Mutex
	hashTable *rendezvous.Table
	waitIndex uint64

	stop chan bool
}

// Join creates a new Node for a given service name and adds it to the
// distributed hash table identified by the given name. The given id should be
// unique among all other nodes with the same name.
func Join(name, id string) (node *Node, err error) {
	node = &Node{
		serviceName: name,
		serviceID:   id,
		stop:        make(chan bool),
	}

	node.consul, err = api.NewClient(api.DefaultConfig())

	if err == nil {
		node.checkListener, node.checkServer, err = newCheckListenerAndServer()
	}

	if err == nil {
		err = node.register()
	}

	if err == nil {
		// Load the initial hash table state. fetchState will not block
		// indefinitely because node.waitIndex is 0.
		state := <-node.fetchState()
		if state.err == nil {
			node.updateState(state)
		} else {
			err = state.err
		}
	}

	if err == nil {
		go node.pollState()
	}

	return node, err
}

func newCheckListenerAndServer() (listener net.Listener, server *http.Server, err error) {
	listener, err = net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return listener, nil, err
	}

	server = &http.Server{
		ReadTimeout:  time.Second,
		WriteTimeout: time.Second,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			fmt.Fprintf(resp, "OK")
		}),
	}

	// When the listener is closed, this goroutine returns.
	go server.Serve(listener)

	return listener, server, err
}

func (n *Node) register() (err error) {
	err = n.consul.Agent().ServiceRegister(&api.AgentServiceRegistration{
		Name: n.serviceName,
		ID:   n.serviceID,
		Check: &api.AgentServiceCheck{
			HTTP:     fmt.Sprintf("http://%s", n.checkListener.Addr().String()),
			Interval: defaultCheckInterval.String(),
		},
	})
	return err
}

type tableState struct {
	index uint64
	ids   []string
	err   error
}

func (n *Node) fetchState() (c chan tableState) {
	c = make(chan tableState)
	go func() {
		state := tableState{}

		opts := &api.QueryOptions{WaitIndex: n.waitIndex}
		services, meta, err := n.consul.Catalog().Service(n.serviceName, "", opts)
		state.err = err

		if state.err == nil {
			state.index = meta.LastIndex

			state.ids = make([]string, len(services))
			for i, service := range services {
				state.ids[i] = service.ServiceID
			}

			// Sanity check
			found := false
			for _, id := range state.ids {
				if id == n.serviceID {
					found = true
					break
				}
			}
			if !found {
				state.err = fmt.Errorf("%s is not in the %s service list: %v", n.serviceID, n.serviceName, state.ids)
			}
		}

		c <- state
	}()
	return c
}

func (n *Node) updateState(state tableState) {
	n.hashMutex.Lock()
	n.hashTable = rendezvous.New(state.ids)
	n.waitIndex = state.index
	n.hashMutex.Unlock()
}

func (n *Node) pollState() {
	var state tableState

	for {
		select {
		case <-n.stop:
			return
		case state = <-n.fetchState():
			if state.err == nil {
				n.updateState(state)
			} else {
				// TODO how do we surface errors?
				time.Sleep(time.Second)
			}
		}
	}
}

// Owns returns true if the given key belongs to this Node in the distributed
// hash table.
func (n *Node) Owns(key string) bool {
	return n.hashTable.Get(key) == n.serviceID
}

// Leave removes this Node from the distributed hash table by de-registering it
// from Consul. Once Leave is called, the Node should be discarded.
func (n *Node) Leave() (err error) {
	close(n.stop) // stop polling for state
	err = n.consul.Agent().ServiceDeregister(n.serviceID)
	n.checkListener.Close() // stop the health check http server
	return err
}
<commit_msg>Update docs.<commit_after>package dht

import (
	"fmt"
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/stvp/rendezvous"
)

const (
	defaultCheckInterval = 5 * time.Second
)

// Node is a single node in a distributed hash table that is coordinated using
// services registered in Consul. Key membership is calculated using rendezvous
// hashing to ensure even distribution of keys and minimal key membership
// changes when a Node fails or otherwise leaves the hash table.
type Node struct {
	// Consul
	serviceName string
	serviceID   string
	consul      *api.Client

	// HTTP health check server
	checkURL      string
	checkListener net.Listener
	checkServer   *http.Server

	// Hash table
	hashMutex sync.Mutex
	hashTable *rendezvous.Table
	waitIndex uint64

	// Graceful shutdown
	stop chan bool
}

// Join creates a new Node for a given service name and adds it to the
// distributed hash table identified by the given name. The given id should be
// unique among all Nodes in the hash table.
func Join(name, id string) (node *Node, err error) {
	node = &Node{
		serviceName: name,
		serviceID:   id,
		stop:        make(chan bool),
	}

	node.consul, err = api.NewClient(api.DefaultConfig())

	if err == nil {
		node.checkListener, node.checkServer, err = newCheckListenerAndServer()
	}

	if err == nil {
		err = node.register()
	}

	if err == nil {
		// Load the initial hash table state. fetchState will not block
		// indefinitely because node.waitIndex is 0.
		state := <-node.fetchState()
		if state.err == nil {
			node.updateState(state)
		} else {
			err = state.err
		}
	}

	if err == nil {
		go node.pollState()
	}

	return node, err
}

func newCheckListenerAndServer() (listener net.Listener, server *http.Server, err error) {
	listener, err = net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return listener, nil, err
	}

	server = &http.Server{
		ReadTimeout:  time.Second,
		WriteTimeout: time.Second,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			fmt.Fprintf(resp, "OK")
		}),
	}

	// When the listener is closed, this goroutine returns.
	go server.Serve(listener)

	return listener, server, err
}

func (n *Node) register() (err error) {
	err = n.consul.Agent().ServiceRegister(&api.AgentServiceRegistration{
		Name: n.serviceName,
		ID:   n.serviceID,
		Check: &api.AgentServiceCheck{
			HTTP:     fmt.Sprintf("http://%s", n.checkListener.Addr().String()),
			Interval: defaultCheckInterval.String(),
		},
	})
	return err
}

type tableState struct {
	index uint64
	ids   []string
	err   error
}

func (n *Node) fetchState() (c chan tableState) {
	c = make(chan tableState)
	go func() {
		state := tableState{}

		opts := &api.QueryOptions{WaitIndex: n.waitIndex}
		services, meta, err := n.consul.Catalog().Service(n.serviceName, "", opts)
		state.err = err

		if state.err == nil {
			state.index = meta.LastIndex

			state.ids = make([]string, len(services))
			for i, service := range services {
				state.ids[i] = service.ServiceID
			}

			// Sanity check
			found := false
			for _, id := range state.ids {
				if id == n.serviceID {
					found = true
					break
				}
			}
			if !found {
				state.err = fmt.Errorf("%s is not in the %s service list: %v", n.serviceID, n.serviceName, state.ids)
			}
		}

		c <- state
	}()
	return c
}

func (n *Node) updateState(state tableState) {
	n.hashMutex.Lock()
	n.hashTable = rendezvous.New(state.ids)
	n.waitIndex = state.index
	n.hashMutex.Unlock()
}

func (n *Node) pollState() {
	var state tableState

	for {
		select {
		case <-n.stop:
			return
		case state = <-n.fetchState():
			if state.err == nil {
				n.updateState(state)
			} else {
				// TODO how do we surface errors?
				time.Sleep(time.Second)
			}
		}
	}
}

// Owns returns true if the given key belongs to this Node in the distributed
// hash table.
func (n *Node) Owns(key string) bool {
	return n.hashTable.Get(key) == n.serviceID
}

// Leave removes the Node from the distributed hash table by de-registering it
// from Consul. Once Leave is called, the Node should be discarded. An error is
// returned if the Node is unable to successfully deregister itself from
// Consul. In that case, Consul's health check for the Node will fail and
// require manual cleanup.
func (n *Node) Leave() (err error) {
	close(n.stop) // stop polling for state
	err = n.consul.Agent().ServiceDeregister(n.serviceID)
	n.checkListener.Close() // stop the health check http server
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/chrisseto/pty"
	"github.com/chrisseto/sux/pansi"
	"github.com/nsf/termbox-go"
	"io"
	"os"
	"os/exec"
)

type Pane struct {
	*exec.Cmd

	cx, cy        int
	sx, sy        int
	fg, bg        termbox.Attribute
	width, height uint16
	scrollOffset  int

	Prog string
	Args []string

	Pty    *os.File
	output io.Reader
	cells  [][]termbox.Cell
}

func CreatePane(width, height uint16, prog string, args ...string) *Pane {
	return &Pane{
		Cmd: exec.Command(prog, args...),
		cx:  0, cy: 0,
		fg: 0, bg: 0,
		scrollOffset: 0,
		drawOffset:   0,
		Prog:         prog, Args: args,
		width: width, height: height,
		Pty: nil,
	}
}

func (p *Pane) Start() error {
	pterm, err := pty.Start(p.Cmd)
	if err != nil {
		panic(err)
	}
	if err = pty.Setsize(pterm, p.height, p.width); err != nil {
		panic(err)
	}
	p.Pty = pterm
	p.cells = make([][]termbox.Cell, 1, p.height)
	p.cells[0] = make([]termbox.Cell, p.width)
	go p.outputPipe()
	return nil
}

func (p *Pane) Close() error {
	return p.Process.Kill()
}

func (p *Pane) Cells() [][]termbox.Cell {
	return p.cells[p.drawOffset:bound(p.drawOffset+int(p.height), p.drawOffset, len(p.cells))]
}

func (p *Pane) Width() uint16 {
	return p.width
}

func (p *Pane) Height() uint16 {
	return p.height
}

func (p *Pane) Scroll(far int) {
	// p.scrollOffset += far
	p.scrollOffset = bound(p.scrollOffset+far, -len(p.cells), 0)
	Redraw()
}

func (p *Pane) bottomLine() *[]termbox.Cell {
	return &p.cells[len(p.cells)-1]
}

func (p *Pane) Redraw() {
	for y, line := range p.Cells() {
		for x, cell := range line {
			termbox.SetCell(x, y, cell.Ch, cell.Fg, cell.Bg)
		}
	}
	termbox.SetCursor(p.Cursor())
}

func bound(val, min, max int) int {
	if val < min {
		return min
	}
	if val > max {
		return max
	}
	return val
}

func (p *Pane) Cursor() (int, int) {
	p.cx = bound(p.cx, 0, int(p.width)-1)
	p.cy = bound(p.cy, 0, int(p.height)-1)
	return p.cx, p.cy
}

func (p *Pane) outputPipe() {
	lexer := pansi.NewLexer()
	buf := make([]byte, 32*1024)
	// f, _ := os.Create("output.log")
	for {
		nr, err := p.Pty.Read(buf)
		if nr > 0 {
			f.Write(buf[:nr])

			for _, char := range buf[:nr] {
				lexer.Feed(char)
				if lexer.State() != pansi.Ground {
					continue
				}
				if res := lexer.Result(); res != nil {
					p.handleEscapeCode(res)
					lexer.Clear()
					continue
				}

				switch char {
				case 0x7: //Terminal Bell. Skip for the moment
				case 0xA:
					p.cy++
					p.cells = append(p.cells, make([]termbox.Cell, p.width))
					row = p.bottomLine()
					if len(p.cells)-p.drawOffset > int(p.height) {
						p.drawOffset++
					}
				case 0xD:
					x, p.cx = 0, 0
				case 0x8:
					if x != 0 {
						x--
						p.cx--
					}
					(*row)[x] = termbox.Cell{' ', p.fg, p.bg}
				default:
					(*row)[x] = termbox.Cell{rune(char), p.fg, p.bg}
					x++
					p.cx++
				}
			}

			Redraw()
		}
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

	}
}

func (p *Pane) handleEscapeCode(c *pansi.AnsiEscapeCode) {
	switch c.Type {
	case pansi.SetGraphicMode:
		p.SetGraphicMode(c.Values)
	case pansi.CursorPosition:
		p.cx, p.cy = c.Values[1], c.Values[2]
	case pansi.CursorUp:
		p.cy--
	case pansi.CursorDown:
		p.cy++
	case pansi.CursorBackward:
		p.cx--
	case pansi.CursorForward:
		p.cx++
	case pansi.EraseLine:
		row := &p.cells[p.sy]
		for i := p.cx; i < len(*row); i++ {
			(*row)[i] = termbox.Cell{' ', p.fg, p.bg}
		}
	case pansi.EraseDisplay:
		p.Clear()
	}
}

func (p *Pane) SetGraphicMode(vals []int) {
	for i := 0; i < len(vals); i++ {
		switch vals[i] {
		case 0:
			p.fg, p.bg = 0, 0
		case 1:
			p.fg |= termbox.AttrBold
		case 38:
			i++
			switch vals[i] {
			case 5:
				i++
				p.fg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 48:
			i++
			switch vals[i] {
			case 5:
				i++
				p.bg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		}
	}
}

func (p *Pane) Clear() {
	p.drawOffset = len(p.cells) - 1
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	Redraw()
}
<commit_msg>Check Result() before checking State()<commit_after>package main

import (
	"github.com/chrisseto/pty"
	"github.com/chrisseto/sux/pansi"
	"github.com/nsf/termbox-go"
	"io"
	"os"
	"os/exec"
)

type Pane struct {
	*exec.Cmd

	cx, cy        int
	sx, sy        int
	fg, bg        termbox.Attribute
	width, height uint16
	scrollOffset  int

	Prog string
	Args []string

	Pty    *os.File
	output io.Reader
	cells  [][]termbox.Cell
}

func CreatePane(width, height uint16, prog string, args ...string) *Pane {
	return &Pane{
		Cmd: exec.Command(prog, args...),
		cx:  0, cy: 0,
		fg: 0, bg: 0,
		scrollOffset: 0,
		drawOffset:   0,
		Prog:         prog, Args: args,
		width: width, height: height,
		Pty: nil,
	}
}

func (p *Pane) Start() error {
	pterm, err := pty.Start(p.Cmd)
	if err != nil {
		panic(err)
	}
	if err = pty.Setsize(pterm, p.height, p.width); err != nil {
		panic(err)
	}
	p.Pty = pterm
	p.cells = make([][]termbox.Cell, 1, p.height)
	p.cells[0] = make([]termbox.Cell, p.width)
	go p.outputPipe()
	return nil
}

func (p *Pane) Close() error {
	return p.Process.Kill()
}

func (p *Pane) Cells() [][]termbox.Cell {
	return p.cells[p.drawOffset:bound(p.drawOffset+int(p.height), p.drawOffset, len(p.cells))]
}

func (p *Pane) Width() uint16 {
	return p.width
}

func (p *Pane) Height() uint16 {
	return p.height
}

func (p *Pane) Scroll(far int) {
	// p.scrollOffset += far
	p.scrollOffset = bound(p.scrollOffset+far, -len(p.cells), 0)
	Redraw()
}

func (p *Pane) bottomLine() *[]termbox.Cell {
	return &p.cells[len(p.cells)-1]
}

func (p *Pane) Redraw() {
	for y, line := range p.Cells() {
		for x, cell := range line {
			termbox.SetCell(x, y, cell.Ch, cell.Fg, cell.Bg)
		}
	}
	termbox.SetCursor(p.Cursor())
}

func bound(val, min, max int) int {
	if val < min {
		return min
	}
	if val > max {
		return max
	}
	return val
}

func (p *Pane) Cursor() (int, int) {
	p.cx = bound(p.cx, 0, int(p.width)-1)
	p.cy = bound(p.cy, 0, int(p.height)-1)
	return p.cx, p.cy
}

func (p *Pane) outputPipe() {
	lexer := pansi.NewLexer()
	buf := make([]byte, 32*1024)
	// f, _ := os.Create("output.log")
	for {
		nr, err := p.Pty.Read(buf)
		if nr > 0 {
			f.Write(buf[:nr])

			for _, char := range buf[:nr] {
				lexer.Feed(char)
				if res := lexer.Result(); res != nil {
					p.handleEscapeCode(res)
					lexer.Clear()
					continue
				}
				if lexer.State() != pansi.Ground {
					continue
				}

				switch char {
				case 0x7: //Terminal Bell. Skip for the moment
				case 0xA:
					p.cy++
					p.cells = append(p.cells, make([]termbox.Cell, p.width))
					row = p.bottomLine()
					if len(p.cells)-p.drawOffset > int(p.height) {
						p.drawOffset++
					}
				case 0xD:
					x, p.cx = 0, 0
				case 0x8:
					if x != 0 {
						x--
						p.cx--
					}
					(*row)[x] = termbox.Cell{' ', p.fg, p.bg}
				default:
					(*row)[x] = termbox.Cell{rune(char), p.fg, p.bg}
					x++
					p.cx++
				}
			}

			Redraw()
		}
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

	}
}

func (p *Pane) handleEscapeCode(c *pansi.AnsiEscapeCode) {
	switch c.Type {
	case pansi.SetGraphicMode:
		p.SetGraphicMode(c.Values)
	case pansi.CursorPosition:
		p.cx, p.cy = c.Values[1], c.Values[2]
	case pansi.CursorUp:
		p.cy--
	case pansi.CursorDown:
		p.cy++
	case pansi.CursorBackward:
		p.cx--
	case pansi.CursorForward:
		p.cx++
	case pansi.EraseLine:
		row := &p.cells[p.sy]
		for i := p.cx; i < len(*row); i++ {
			(*row)[i] = termbox.Cell{' ', p.fg, p.bg}
		}
	case pansi.EraseDisplay:
		p.Clear()
	}
}

func (p *Pane) SetGraphicMode(vals []int) {
	for i := 0; i < len(vals); i++ {
		switch vals[i] {
		case 0:
			p.fg, p.bg = 0, 0
		case 1:
			p.fg |= termbox.AttrBold
		case 38:
			i++
			switch vals[i] {
			case 5:
				i++
				p.fg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 48:
			i++
			switch vals[i] {
			case 5:
				i++
				p.bg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		}
	}
}

func (p *Pane) Clear() {
	p.drawOffset = len(p.cells) - 1
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	Redraw()
}
<|endoftext|>


<commit_before>package gopath

import (
	"reflect"
	"strings"
)

type Iter struct {
	values []reflect.Value
	index  int
	next   reflect.Value
}

func (self *Iter) Next() bool {
	self.next = reflect.Value{}
	if self.index >= len(self.values) {
		return false
	}
	self.next = self.values[self.index]
	self.index += 1
	return true
}

func (self *Iter) Value() interface{} {
	if self.next.IsValid() {
		return self.next.Interface()
	}
	return nil
}

type Path struct {
	path []string
}

func NewPath(path string) *Path {
	if path == "" {
		path = "*"
	} else {
		path = strings.Trim(path, "/")
	}

	splitPath := strings.Split(path, "/")

	p := &Path{path: splitPath}
	return p
}

func (self *Path) Iter(container interface{}) *Iter {
	i := &Iter{}
	i.find(self.path, reflect.ValueOf(container))
	return i
}

func (self *Path) First(container interface{}) (interface{}, bool) {
	i := self.Iter(container)
	if i.Next() {
		return i.Value(), true
	} else {
		return nil, false
	}
}

func (self *Iter) appendValue(val reflect.Value) {
	if !val.IsValid() {
		return
	}
	if val.Kind() == reflect.Ptr {
		val = reflect.Indirect(val)
	}

	// if val.Kind() == reflect.Struct {
	// 	if val.CanAddr() {
	// 		*results = append(*results, val.Addr().Interface())
	// 		return
	// 	}
	// }
	self.values = append(self.values, val)
}

func (self *Iter) find(path []string, val reflect.Value) {
	pv := val
	if pv.Kind() == reflect.Ptr {
		if pv.IsNil() {
			return
		}
		val = pv.Elem()
	}

	if len(path) == 0 {
		self.appendValue(pv)
		return
	}

	switch val.Kind() {
	case reflect.Struct:
		self.findStruct(path, pv)
	case reflect.Slice:
		self.findSlice(path, pv)
	case reflect.Map:
		self.findMap(path, pv)
	}
}

func (self *Iter) findStruct(path []string, val reflect.Value) {
	pv := val
	if pv.Kind() == reflect.Ptr {
		if pv.IsNil() {
			pv.Set(reflect.New(pv.Type().Elem()))
		}
		val = pv.Elem()
	}

	t := val.Type()

	switch {
	case path[0] == "**":
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			value := val.Field(i)
			if field.Anonymous {
				self.find(path, value)
				continue
			}
			if path[1] == field.Name {
				self.find(path[2:], value)
			} else {
				self.find(path, value)
			}
		}
	case path[0] == "*":
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			value := val.Field(i)
			if field.Anonymous {
				self.find(path, value)
				continue
			}

			self.find(path[1:], value)
		}
	default:
		value := val.FieldByName(path[0])
		if value.IsValid() {
			self.find(path[1:], value)
		}
	}
}

func (self *Iter) findMap(path []string, val reflect.Value) {
	pv := val
	if pv.Kind() == reflect.Ptr {
		if pv.IsNil() {
			pv.Set(reflect.New(pv.Type().Elem()))
		}
		val = pv.Elem()
	}

	switch {
	case path[0] == "**":
		for _, key := range val.MapKeys() {
			value := val.MapIndex(key)
			if path[1] == key.String() {
				self.find(path[2:], value)
			} else {
				self.find(path, value)
			}
		}
	case path[0] == "*":
		for _, key := range val.MapKeys() {
			value := val.MapIndex(key)
			self.find(path[1:], value)
		}
	default:
		value := val.MapIndex(reflect.ValueOf(path[0]))
		if value.IsValid() {
			self.find(path[1:], value)
		}
	}
}

func (self *Iter) findSlice(path []string, val reflect.Value) {
	pv := val
	if pv.Kind() == reflect.Ptr {
		if pv.IsNil() {
			pv.Set(reflect.New(pv.Type().Elem()))
		}
		val = pv.Elem()
	}

	switch {
	case path[0] == "**":
		for i := 0; i < val.Len(); i++ {
			value := val.Index(i)
			self.find(path, value)
		}
	case path[0] == "*":
		for i := 0; i < val.Len(); i++ {
			value := val.Index(i)
			self.find(path[1:], value)
		}
	}

}
<commit_msg>Got working<commit_after>package gopath

import (
	"reflect"
	"strings"
)

type Iter struct {
	values []reflect.Value
	index  int
	next   reflect.Value
}

func (self *Iter) Next() bool {
	self.next = reflect.Value{}
	if self.index >= len(self.values) {
		return false
	}
	self.next = self.values[self.index]
	self.index += 1
	return true
}

func (self *Iter) Value() interface{} {
	if self.next.IsValid() {
		return self.next.Interface()
	}
	return nil
}

type Path struct {
	path []string
}

func NewPath(path string) *Path {
	if path == "" {
		path = "*"
	} else {
		path = strings.Trim(path, "/")
	}

	splitPath := strings.Split(path, "/")

	p := &Path{path: splitPath}
	return p
}

func (self *Path) Iter(container interface{}) *Iter {
	i := &Iter{}
	i.find(self.path, reflect.ValueOf(container))
	return i
}

func (self *Path) First(container interface{}) (interface{}, bool) {
	i := self.Iter(container)
	if i.Next() {
		return i.Value(), true
	} else {
		return nil, false
	}
}

func (self *Iter) appendValue(val reflect.Value) {
	if !val.IsValid() {
		return
	}
	if val.Kind() == reflect.Ptr {
		val = reflect.Indirect(val)
	}

	// if val.Kind() == reflect.Struct {
	// 	if val.CanAddr() {
	// 		*results = append(*results, val.Addr().Interface())
	// 		return
	// 	}
	// }
	self.values = append(self.values, val)
}

func (self *Iter) find(path []string, val reflect.Value) {

	if val.Kind() == reflect.Ptr {
		if val.IsNil() {
			return
		}
		val = reflect.Indirect(val)
	}

	if len(path) == 0 {
		self.appendValue(val)
		return
	}

	switch val.Kind() {
	case reflect.Struct:
		self.findStruct(path, val)
	case reflect.Slice:
		self.findSlice(path, val)
	case reflect.Map:
		self.findMap(path, val)
	}
}

func (self *Iter) findStruct(path []string, val reflect.Value) {

	t := val.Type()

	switch {
	case path[0] == "**":
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			value := val.Field(i)
			if field.Anonymous {
				self.find(path, value)
				continue
			}
			if path[1] == field.Name {
				self.find(path[2:], value)
			} else {
				self.find(path, value)
			}
		}
	case path[0] == "*":
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			value := val.Field(i)
			if field.Anonymous {
				self.find(path, value)
				continue
			}

			self.find(path[1:], value)
		}
	default:
		value := val.FieldByName(path[0])
		if value.IsValid() {
			self.find(path[1:], value)
		}
	}
}

func (self *Iter) findMap(path []string, val reflect.Value) {

	switch {
	case path[0] == "**":
		for _, key := range val.MapKeys() {
			value := val.MapIndex(key)
			if path[1] == key.String() {
				self.find(path[2:], value)
			} else {
				self.find(path, value)
			}
		}
	case path[0] == "*":
		for _, key := range val.MapKeys() {
			value := val.MapIndex(key)
			self.find(path[1:], value)
		}
	default:
		value := val.MapIndex(reflect.ValueOf(path[0]))
		if value.IsValid() {
			self.find(path[1:], value)
		}
	}
}

func (self *Iter) findSlice(path []string, val reflect.Value) {

	switch {
	case path[0] == "**":
		for i := 0; i < val.Len(); i++ {
			value := val.Index(i)
			self.find(path, value)
		}
	case path[0] == "*":
		for i := 0; i < val.Len(); i++ {
			value := val.Index(i)
			self.find(path[1:], value)
		}
	}

}
<|endoftext|>


<commit_before>// Package pcap is a wrapper around the pcap library.
package pcap

/*
#cgo LDFLAGS: -lpcap
#include <stdlib.h>
#include <pcap.h>

// Workaround for not knowing how to cast to const u_char**
int hack_pcap_next_ex(pcap_t * p, struct pcap_pkthdr **pkt_header,
		      u_char ** pkt_data)
{
	return pcap_next_ex(p, pkt_header, (const u_char **)pkt_data);
}
*/
import "C"

import (
	"errors"
	"net"
	"syscall"
	"time"
	"unsafe"
)

type Pcap struct {
	cptr *C.pcap_t
}

type pcapError struct{ string }

type Stat struct {
	PacketsReceived  uint32
	PacketsDropped   uint32
	PacketsIfDropped uint32
}

type Interface struct {
	Name        string
	Description string
	Addresses   []IFAddress
	// TODO: add more elements
}

type IFAddress struct {
	IP      net.IP
	Netmask net.IPMask
	// TODO: add broadcast + PtP dst ?
}

func Version() string               { return C.GoString(C.pcap_lib_version()) }
func (p *Pcap) Datalink() int       { return int(C.pcap_datalink(p.cptr)) }
func (e *pcapError) Error() string  { return e.string }
func (p *Pcap) Geterror() error     { return &pcapError{C.GoString(C.pcap_geterr(p.cptr))} }
func (p *Pcap) Next() (pkt *Packet) { rv, _ := p.NextEx(); return rv }

func Create(device string) (handle *Pcap, err error) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	h := new(Pcap)

	dev := C.CString(device)
	defer C.free(unsafe.Pointer(dev))

	h.cptr = C.pcap_create(dev, buf)
	if nil == h.cptr {
		handle = nil
		err = &pcapError{C.GoString(buf)}
	} else {
		handle = h
	}

	C.free(unsafe.Pointer(buf))
	return
}

// Set buffer size (units in bytes) on activated handle.
func (p *Pcap) SetBufferSize(sz int32) error {
	if C.pcap_set_buffer_size(p.cptr, C.int(sz)) != 0 {
		return p.Geterror()
	}
	return nil
}

//  If arg p is non-zero promiscuous mode will be set on capture handle when it is activated.
func (p *Pcap) SetPromisc(promisc bool) error {
	var pro int32
	if promisc {
		pro = 1
	}

	if C.pcap_set_promisc(p.cptr, C.int(pro)) != 0 {
		return p.Geterror()
	}
	return nil
}

func (p *Pcap) SetSnapLen(s int32) error {
	if C.pcap_set_snaplen(p.cptr, C.int(s)) != 0 {
		return p.Geterror()
	}
	return nil
}

// Set read timeout (milliseconds) that will be used on a capture handle when it is activated.
func (p *Pcap) SetReadTimeout(toMs int32) error {
	if C.pcap_set_timeout(p.cptr, C.int(toMs)) != 0 {
		return p.Geterror()
	}
	return nil
}

// Activate a packet capture handle to look at packets on the network, with the options that
// were set on the handle being in effect.
func (p *Pcap) Activate() error {
	if C.pcap_activate(p.cptr) != 0 {
		return p.Geterror()
	}
	return nil
}

// OpenLive opens a device and returns a handler.
func OpenLive(device string, snaplen int32, promisc bool, timeout_ms int32) (handle *Pcap, err error) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	h := new(Pcap)
	var pro int32
	if promisc {
		pro = 1
	}

	dev := C.CString(device)
	defer C.free(unsafe.Pointer(dev))

	h.cptr = C.pcap_open_live(dev, C.int(snaplen), C.int(pro), C.int(timeout_ms), buf)
	if nil == h.cptr {
		handle = nil
		err = &pcapError{C.GoString(buf)}
	} else {
		handle = h
	}
	C.free(unsafe.Pointer(buf))
	return
}

// Openoffline
func OpenOffline(file string) (handle *Pcap, err error) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	h := new(Pcap)

	cf := C.CString(file)
	defer C.free(unsafe.Pointer(cf))

	h.cptr = C.pcap_open_offline(cf, buf)
	if nil == h.cptr {
		handle = nil
		err = &pcapError{C.GoString(buf)}
	} else {
		handle = h
	}
	C.free(unsafe.Pointer(buf))
	return
}

// Pcap closes a handler.
func (p *Pcap) Close() {
	C.pcap_close(p.cptr)
}

func (p *Pcap) NextEx() (pkt *Packet, result int32) {
	var pkthdr_ptr *C.struct_pcap_pkthdr
	var pkthdr C.struct_pcap_pkthdr

	var buf_ptr *C.u_char
	var buf unsafe.Pointer
	result = int32(C.hack_pcap_next_ex(p.cptr, &pkthdr_ptr, &buf_ptr))

	buf = unsafe.Pointer(buf_ptr)
	pkthdr = *pkthdr_ptr

	if nil == buf {
		return
	}
	pkt = new(Packet)
	pkt.Time = time.Unix(int64(pkthdr.ts.tv_sec), int64(pkthdr.ts.tv_usec))
	pkt.Caplen = uint32(pkthdr.caplen)
	pkt.Len = uint32(pkthdr.len)
	pkt.Data = make([]byte, pkthdr.caplen)

	for i := uint32(0); i < pkt.Caplen; i++ {
		pkt.Data[i] = *(*byte)(unsafe.Pointer(uintptr(buf) + uintptr(i)))
	}
	return
}

func (p *Pcap) Getstats() (stat *Stat, err error) {
	var cstats _Ctype_struct_pcap_stat
	if -1 == C.pcap_stats(p.cptr, &cstats) {
		return nil, p.Geterror()
	}
	stats := new(Stat)
	stats.PacketsReceived = uint32(cstats.ps_recv)
	stats.PacketsDropped = uint32(cstats.ps_drop)
	stats.PacketsIfDropped = uint32(cstats.ps_ifdrop)

	return stats, nil
}

func (p *Pcap) SetFilter(expr string) (err error) {
	var bpf _Ctype_struct_bpf_program
	cexpr := C.CString(expr)
	defer C.free(unsafe.Pointer(cexpr))

	if -1 == C.pcap_compile(p.cptr, &bpf, cexpr, 1, 0) {
		return p.Geterror()
	}

	if -1 == C.pcap_setfilter(p.cptr, &bpf) {
		C.pcap_freecode(&bpf)
		return p.Geterror()
	}
	C.pcap_freecode(&bpf)
	return nil
}

func (p *Pcap) SetDataLink(dlt int) error {
	if -1 == C.pcap_set_datalink(p.cptr, C.int(dlt)) {
		return p.Geterror()
	}
	return nil
}

func DatalinkValueToName(dlt int) string {
	if name := C.pcap_datalink_val_to_name(C.int(dlt)); name != nil {
		return C.GoString(name)
	}
	return ""
}

func DatalinkValueToDescription(dlt int) string {
	if desc := C.pcap_datalink_val_to_description(C.int(dlt)); desc != nil {
		return C.GoString(desc)
	}
	return ""
}

func FindAllDevs() (ifs []Interface, err string) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	defer C.free(unsafe.Pointer(buf))
	var alldevsp *C.pcap_if_t

	if -1 == C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp), buf) {
		return nil, C.GoString(buf)
	}
	defer C.pcap_freealldevs((*C.pcap_if_t)(alldevsp))
	dev := alldevsp
	var i uint32
	for i = 0; dev != nil; dev = (*C.pcap_if_t)(dev.next) {
		i++
	}
	ifs = make([]Interface, i)
	dev = alldevsp
	for j := uint32(0); dev != nil; dev = (*C.pcap_if_t)(dev.next) {
		var iface Interface
		iface.Name = C.GoString(dev.name)
		iface.Description = C.GoString(dev.description)
		iface.Addresses = findAllAddresses(dev.addresses)
		// TODO: add more elements
		ifs[j] = iface
		j++
	}
	return
}

func findAllAddresses(addresses *_Ctype_struct_pcap_addr) (retval []IFAddress) {
	// TODO - make it support more than IPv4 and IPv6?
	retval = make([]IFAddress, 0, 1)
	for curaddr := addresses; curaddr != nil; curaddr = (*_Ctype_struct_pcap_addr)(curaddr.next) {
		var a IFAddress
		var err error
		if a.IP, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.addr))); err != nil {
			continue
		}
		if a.Netmask, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.addr))); err != nil {
			continue
		}
		retval = append(retval, a)
	}
	return
}

func sockaddrToIP(rsa *syscall.RawSockaddr) (IP []byte, err error) {
	switch rsa.Family {
	case syscall.AF_INET:
		pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa))
		IP = make([]byte, 4)
		for i := 0; i < len(IP); i++ {
			IP[i] = pp.Addr[i]
		}
		return
	case syscall.AF_INET6:
		pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa))
		IP = make([]byte, 16)
		for i := 0; i < len(IP); i++ {
			IP[i] = pp.Addr[i]
		}
		return
	}
	err = errors.New("Unsupported address type")
	return
}

// Inject ...
func (p *Pcap) Inject(data []byte) (err error) {
	buf := (*C.char)(C.malloc((C.size_t)(len(data))))

	for i := 0; i < len(data); i++ {
		*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + uintptr(i))) = data[i]
	}

	if -1 == C.pcap_inject(p.cptr, unsafe.Pointer(buf), (C.size_t)(len(data))) {
		err = p.Geterror()
	}
	C.free(unsafe.Pointer(buf))
	return
}
<commit_msg>Fix nil pointer derefernce for tunnel device<commit_after>// Package pcap is a wrapper around the pcap library.
package pcap

/*
#cgo LDFLAGS: -lpcap
#include <stdlib.h>
#include <pcap.h>

// Workaround for not knowing how to cast to const u_char**
int hack_pcap_next_ex(pcap_t * p, struct pcap_pkthdr **pkt_header,
		      u_char ** pkt_data)
{
	return pcap_next_ex(p, pkt_header, (const u_char **)pkt_data);
}
*/
import "C"

import (
	"errors"
	"net"
	"syscall"
	"time"
	"unsafe"
)

type Pcap struct {
	cptr *C.pcap_t
}

type pcapError struct{ string }

type Stat struct {
	PacketsReceived  uint32
	PacketsDropped   uint32
	PacketsIfDropped uint32
}

type Interface struct {
	Name        string
	Description string
	Addresses   []IFAddress
	// TODO: add more elements
}

type IFAddress struct {
	IP      net.IP
	Netmask net.IPMask
	// TODO: add broadcast + PtP dst ?
}

func Version() string               { return C.GoString(C.pcap_lib_version()) }
func (p *Pcap) Datalink() int       { return int(C.pcap_datalink(p.cptr)) }
func (e *pcapError) Error() string  { return e.string }
func (p *Pcap) Geterror() error     { return &pcapError{C.GoString(C.pcap_geterr(p.cptr))} }
func (p *Pcap) Next() (pkt *Packet) { rv, _ := p.NextEx(); return rv }

func Create(device string) (handle *Pcap, err error) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	h := new(Pcap)

	dev := C.CString(device)
	defer C.free(unsafe.Pointer(dev))

	h.cptr = C.pcap_create(dev, buf)
	if nil == h.cptr {
		handle = nil
		err = &pcapError{C.GoString(buf)}
	} else {
		handle = h
	}

	C.free(unsafe.Pointer(buf))
	return
}

// Set buffer size (units in bytes) on activated handle.
func (p *Pcap) SetBufferSize(sz int32) error {
	if C.pcap_set_buffer_size(p.cptr, C.int(sz)) != 0 {
		return p.Geterror()
	}
	return nil
}

//  If arg p is non-zero promiscuous mode will be set on capture handle when it is activated.
func (p *Pcap) SetPromisc(promisc bool) error {
	var pro int32
	if promisc {
		pro = 1
	}

	if C.pcap_set_promisc(p.cptr, C.int(pro)) != 0 {
		return p.Geterror()
	}
	return nil
}

func (p *Pcap) SetSnapLen(s int32) error {
	if C.pcap_set_snaplen(p.cptr, C.int(s)) != 0 {
		return p.Geterror()
	}
	return nil
}

// Set read timeout (milliseconds) that will be used on a capture handle when it is activated.
func (p *Pcap) SetReadTimeout(toMs int32) error {
	if C.pcap_set_timeout(p.cptr, C.int(toMs)) != 0 {
		return p.Geterror()
	}
	return nil
}

// Activate a packet capture handle to look at packets on the network, with the options that
// were set on the handle being in effect.
func (p *Pcap) Activate() error {
	if C.pcap_activate(p.cptr) != 0 {
		return p.Geterror()
	}
	return nil
}

// OpenLive opens a device and returns a handler.
func OpenLive(device string, snaplen int32, promisc bool, timeout_ms int32) (handle *Pcap, err error) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	h := new(Pcap)
	var pro int32
	if promisc {
		pro = 1
	}

	dev := C.CString(device)
	defer C.free(unsafe.Pointer(dev))

	h.cptr = C.pcap_open_live(dev, C.int(snaplen), C.int(pro), C.int(timeout_ms), buf)
	if nil == h.cptr {
		handle = nil
		err = &pcapError{C.GoString(buf)}
	} else {
		handle = h
	}
	C.free(unsafe.Pointer(buf))
	return
}

// Openoffline
func OpenOffline(file string) (handle *Pcap, err error) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	h := new(Pcap)

	cf := C.CString(file)
	defer C.free(unsafe.Pointer(cf))

	h.cptr = C.pcap_open_offline(cf, buf)
	if nil == h.cptr {
		handle = nil
		err = &pcapError{C.GoString(buf)}
	} else {
		handle = h
	}
	C.free(unsafe.Pointer(buf))
	return
}

// Pcap closes a handler.
func (p *Pcap) Close() {
	C.pcap_close(p.cptr)
}

func (p *Pcap) NextEx() (pkt *Packet, result int32) {
	var pkthdr_ptr *C.struct_pcap_pkthdr
	var pkthdr C.struct_pcap_pkthdr

	var buf_ptr *C.u_char
	var buf unsafe.Pointer
	result = int32(C.hack_pcap_next_ex(p.cptr, &pkthdr_ptr, &buf_ptr))

	buf = unsafe.Pointer(buf_ptr)
	pkthdr = *pkthdr_ptr

	if nil == buf {
		return
	}
	pkt = new(Packet)
	pkt.Time = time.Unix(int64(pkthdr.ts.tv_sec), int64(pkthdr.ts.tv_usec))
	pkt.Caplen = uint32(pkthdr.caplen)
	pkt.Len = uint32(pkthdr.len)
	pkt.Data = make([]byte, pkthdr.caplen)

	for i := uint32(0); i < pkt.Caplen; i++ {
		pkt.Data[i] = *(*byte)(unsafe.Pointer(uintptr(buf) + uintptr(i)))
	}
	return
}

func (p *Pcap) Getstats() (stat *Stat, err error) {
	var cstats _Ctype_struct_pcap_stat
	if -1 == C.pcap_stats(p.cptr, &cstats) {
		return nil, p.Geterror()
	}
	stats := new(Stat)
	stats.PacketsReceived = uint32(cstats.ps_recv)
	stats.PacketsDropped = uint32(cstats.ps_drop)
	stats.PacketsIfDropped = uint32(cstats.ps_ifdrop)

	return stats, nil
}

func (p *Pcap) SetFilter(expr string) (err error) {
	var bpf _Ctype_struct_bpf_program
	cexpr := C.CString(expr)
	defer C.free(unsafe.Pointer(cexpr))

	if -1 == C.pcap_compile(p.cptr, &bpf, cexpr, 1, 0) {
		return p.Geterror()
	}

	if -1 == C.pcap_setfilter(p.cptr, &bpf) {
		C.pcap_freecode(&bpf)
		return p.Geterror()
	}
	C.pcap_freecode(&bpf)
	return nil
}

func (p *Pcap) SetDataLink(dlt int) error {
	if -1 == C.pcap_set_datalink(p.cptr, C.int(dlt)) {
		return p.Geterror()
	}
	return nil
}

func DatalinkValueToName(dlt int) string {
	if name := C.pcap_datalink_val_to_name(C.int(dlt)); name != nil {
		return C.GoString(name)
	}
	return ""
}

func DatalinkValueToDescription(dlt int) string {
	if desc := C.pcap_datalink_val_to_description(C.int(dlt)); desc != nil {
		return C.GoString(desc)
	}
	return ""
}

func FindAllDevs() (ifs []Interface, err string) {
	var buf *C.char
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1))
	defer C.free(unsafe.Pointer(buf))
	var alldevsp *C.pcap_if_t

	if -1 == C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp), buf) {
		return nil, C.GoString(buf)
	}
	defer C.pcap_freealldevs((*C.pcap_if_t)(alldevsp))
	dev := alldevsp
	var i uint32
	for i = 0; dev != nil; dev = (*C.pcap_if_t)(dev.next) {
		i++
	}
	ifs = make([]Interface, i)
	dev = alldevsp
	for j := uint32(0); dev != nil; dev = (*C.pcap_if_t)(dev.next) {
		var iface Interface
		iface.Name = C.GoString(dev.name)
		iface.Description = C.GoString(dev.description)
		iface.Addresses = findAllAddresses(dev.addresses)
		// TODO: add more elements
		ifs[j] = iface
		j++
	}
	return
}

func findAllAddresses(addresses *_Ctype_struct_pcap_addr) (retval []IFAddress) {
	// TODO - make it support more than IPv4 and IPv6?
	retval = make([]IFAddress, 0, 1)
	for curaddr := addresses; curaddr != nil; curaddr = (*_Ctype_struct_pcap_addr)(curaddr.next) {
		if curaddr.addr == nil {
			continue
		}
		var a IFAddress
		var err error
		if a.IP, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.addr))); err != nil {
			continue
		}
		if a.Netmask, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.addr))); err != nil {
			continue
		}
		retval = append(retval, a)
	}
	return
}

func sockaddrToIP(rsa *syscall.RawSockaddr) (IP []byte, err error) {
	switch rsa.Family {
	case syscall.AF_INET:
		pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa))
		IP = make([]byte, 4)
		for i := 0; i < len(IP); i++ {
			IP[i] = pp.Addr[i]
		}
		return
	case syscall.AF_INET6:
		pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa))
		IP = make([]byte, 16)
		for i := 0; i < len(IP); i++ {
			IP[i] = pp.Addr[i]
		}
		return
	}
	err = errors.New("Unsupported address type")
	return
}

// Inject ...
func (p *Pcap) Inject(data []byte) (err error) {
	buf := (*C.char)(C.malloc((C.size_t)(len(data))))

	for i := 0; i < len(data); i++ {
		*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + uintptr(i))) = data[i]
	}

	if -1 == C.pcap_inject(p.cptr, unsafe.Pointer(buf), (C.size_t)(len(data))) {
		err = p.Geterror()
	}
	C.free(unsafe.Pointer(buf))
	return
}
<|endoftext|>


<commit_before>package checkpostgresql

import (
	"fmt"
	"os"
	"strings"

	// PostgreSQL Driver
	_ "github.com/lib/pq"
	"github.com/mackerelio/checkers"
)

var commands = map[string](func([]string) *checkers.Checker){
	"connection": checkConnection,
}

type postgresqlSetting struct {
	Host     string `short:"H" long:"host" default:"localhost" description:"Hostname"`
	Port     string `short:"p" long:"port" default:"5432" description:"Port"`
	User     string `short:"u" long:"user" default:"postgres" description:"Username"`
	Password string `short:"P" long:"password" default:"" description:"Password"`
	SSLmode  string `short:"s" long:"sslmode" default:"disable" description:"SSLmode"`
	Timeout  int    `short:"t" long:"timeout" default:"5" description:"Maximum wait for connection, in seconds."`
}

func (p postgresqlSetting) getDriverAndDataSourceName() (string, string) {
	dataSourceName := fmt.Sprintf("user=%s password=%s host=%s port=%s sslmode=%s connect_timeout=%d", p.User, p.Password, p.Host, p.Port, p.SSLmode, p.Timeout)
	return "postgres", dataSourceName
}

func separateSub(argv []string) (string, []string) {
	if len(argv) == 0 || strings.HasPrefix(argv[0], "-") {
		return "", argv
	}
	return argv[0], argv[1:]
}

// Do the plugin
func Do() {
	subCmd, argv := separateSub(os.Args[1:])
	fn, ok := commands[subCmd]
	if !ok {
		fmt.Println(`Usage:
  check-postgresql [subcommand] [OPTIONS]

SubCommands:`)
		for k := range commands {
			fmt.Printf("  %s\n", k)
		}
		os.Exit(1)
	}
	ckr := fn(argv)
	ckr.Name = fmt.Sprintf("PostgreSQL %s", strings.Title(subCmd))
	ckr.Exit()
}
<commit_msg>Add dbname to postgresql-setting<commit_after>package checkpostgresql

import (
	"fmt"
	"os"
	"strings"

	// PostgreSQL Driver
	_ "github.com/lib/pq"
	"github.com/mackerelio/checkers"
)

var commands = map[string](func([]string) *checkers.Checker){
	"connection": checkConnection,
}

type postgresqlSetting struct {
	Host     string `short:"H" long:"host" default:"localhost" description:"Hostname"`
	Port     string `short:"p" long:"port" default:"5432" description:"Port"`
	User     string `short:"u" long:"user" default:"postgres" description:"Username"`
	Password string `short:"P" long:"password" default:"" description:"Password"`
	Database string `short:"d" long:"dbname" description:"DBname"`
	SSLmode  string `short:"s" long:"sslmode" default:"disable" description:"SSLmode"`
	Timeout  int    `short:"t" long:"timeout" default:"5" description:"Maximum wait for connection, in seconds."`
}

func (p postgresqlSetting) getDriverAndDataSourceName() (string, string) {
	dataSourceName := fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s connect_timeout=%d", p.User, p.Password, p.Host, p.Port, p.Database, p.SSLmode, p.Timeout)
	return "postgres", dataSourceName
}

func separateSub(argv []string) (string, []string) {
	if len(argv) == 0 || strings.HasPrefix(argv[0], "-") {
		return "", argv
	}
	return argv[0], argv[1:]
}

// Do the plugin
func Do() {
	subCmd, argv := separateSub(os.Args[1:])
	fn, ok := commands[subCmd]
	if !ok {
		fmt.Println(`Usage:
  check-postgresql [subcommand] [OPTIONS]

SubCommands:`)
		for k := range commands {
			fmt.Printf("  %s\n", k)
		}
		os.Exit(1)
	}
	ckr := fn(argv)
	ckr.Name = fmt.Sprintf("PostgreSQL %s", strings.Title(subCmd))
	ckr.Exit()
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 (
	"github.com/prometheus/client_golang/prometheus"
)

var (
	authorizerErrorMetric = prometheus.NewCounter(
		prometheus.CounterOpts{
			Name: "node_authorizer_error_counter",
			Help: "The number of errors encountered by the authorizer",
		},
	)
	authorizeRequestLatencyMetric = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Name: "node_request_latency_seconds",
			Help: "A summary of the latency for incoming node authorization requests",
		},
	)
	authorizerLatencyMetric = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Name: "node_authorizer_latercy_seconds",
			Help: "A summary of the latency for the authorizer in seconds",
		},
	)
	nodeAuthorizationMetric = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "node_authorizer_counter",
			Help: "A counter of number node authorizations broken down by denied and allowed",
		},
		[]string{"action"},
	)
	tokenLatencyMetric = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Name: "token_latency_seconds",
			Help: "A summary of the latency experienced when creating bootstrap tokens in seconds",
		},
	)
)

func init() {
	prometheus.MustRegister(authorizeRequestLatencyMetric)
	prometheus.MustRegister(authorizerErrorMetric)
	prometheus.MustRegister(authorizerLatencyMetric)
	prometheus.MustRegister(nodeAuthorizationMetric)
	prometheus.MustRegister(tokenLatencyMetric)
}
<commit_msg>Fix typo on node-authorizer prometheus metric<commit_after>/*
Copyright 2018 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 (
	"github.com/prometheus/client_golang/prometheus"
)

var (
	authorizerErrorMetric = prometheus.NewCounter(
		prometheus.CounterOpts{
			Name: "node_authorizer_error_counter",
			Help: "The number of errors encountered by the authorizer",
		},
	)
	authorizeRequestLatencyMetric = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Name: "node_request_latency_seconds",
			Help: "A summary of the latency for incoming node authorization requests",
		},
	)
	authorizerLatencyMetric = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Name: "node_authorizer_latency_seconds",
			Help: "A summary of the latency for the authorizer in seconds",
		},
	)
	nodeAuthorizationMetric = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "node_authorizer_counter",
			Help: "A counter of number node authorizations broken down by denied and allowed",
		},
		[]string{"action"},
	)
	tokenLatencyMetric = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Name: "token_latency_seconds",
			Help: "A summary of the latency experienced when creating bootstrap tokens in seconds",
		},
	)
)

func init() {
	prometheus.MustRegister(authorizeRequestLatencyMetric)
	prometheus.MustRegister(authorizerErrorMetric)
	prometheus.MustRegister(authorizerLatencyMetric)
	prometheus.MustRegister(nodeAuthorizationMetric)
	prometheus.MustRegister(tokenLatencyMetric)
}
<|endoftext|>


<commit_before>package policy

import (
	"errors"
	"fmt"
	"io"

	"github.com/spf13/cobra"

	kapi "k8s.io/kubernetes/pkg/api"
	kapierrors "k8s.io/kubernetes/pkg/api/errors"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/util/sets"

	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
	"github.com/openshift/origin/pkg/authorization/rulevalidation"
	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
	cmdutil "github.com/openshift/origin/pkg/cmd/util"
	osutil "github.com/openshift/origin/pkg/cmd/util"
	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
)

// ReconcileClusterRolesRecommendedName is the recommended command name
const ReconcileClusterRolesRecommendedName = "reconcile-cluster-roles"

type ReconcileClusterRolesOptions struct {
	// RolesToReconcile says which roles should be reconciled.  An empty or nil slice means
	// reconcile all of them.
	RolesToReconcile []string

	Confirmed bool
	Union     bool

	Out    io.Writer
	Output string

	RoleClient client.ClusterRoleInterface
}

const (
	reconcileLong = `
Replace cluster roles to match the recommended bootstrap policy

This command will inspect the cluster roles against the recommended bootstrap policy.  Any cluster role
that does not match will be replaced by the recommended bootstrap role.  This command will not remove
any additional cluster role.

You can see which cluster role have recommended changed by choosing an output type.`

	reconcileExample = `  # Display the cluster roles that would be modified
  $ %[1]s

  # Replace cluster roles that don't match the current defaults
  $ %[1]s --confirm

  # Display the union of the default and modified cluster roles
  $ %[1]s --additive-only`
)

// NewCmdReconcileClusterRoles implements the OpenShift cli reconcile-cluster-roles command
func NewCmdReconcileClusterRoles(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	o := &ReconcileClusterRolesOptions{
		Out:   out,
		Union: true,
	}

	cmd := &cobra.Command{
		Use:     name + " [ClusterRoleName]...",
		Short:   "Replace cluster roles to match the recommended bootstrap policy",
		Long:    reconcileLong,
		Example: fmt.Sprintf(reconcileExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			if err := o.Complete(cmd, f, args); err != nil {
				kcmdutil.CheckErr(err)
			}

			if err := o.Validate(); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
			}

			if err := o.RunReconcileClusterRoles(cmd, f); err != nil {
				kcmdutil.CheckErr(err)
			}
		},
	}

	cmd.Flags().BoolVar(&o.Confirmed, "confirm", o.Confirmed, "Specify that cluster roles should be modified. Defaults to false, displaying what would be replaced but not actually replacing anything.")
	cmd.Flags().BoolVar(&o.Union, "additive-only", o.Union, "Preserves modified cluster roles.")
	kcmdutil.AddPrinterFlags(cmd)
	cmd.Flags().Lookup("output").DefValue = "yaml"
	cmd.Flags().Lookup("output").Value.Set("yaml")

	return cmd
}

func (o *ReconcileClusterRolesOptions) Complete(cmd *cobra.Command, f *clientcmd.Factory, args []string) error {
	oclient, _, err := f.Clients()
	if err != nil {
		return err
	}
	o.RoleClient = oclient.ClusterRoles()

	o.Output = kcmdutil.GetFlagString(cmd, "output")

	mapper, _ := f.Object(false)
	for _, resourceString := range args {
		resource, name, err := osutil.ResolveResource(authorizationapi.Resource("clusterroles"), resourceString, mapper)
		if err != nil {
			return err
		}
		if resource != authorizationapi.Resource("clusterroles") {
			return fmt.Errorf("%v is not a valid resource type for this command", resource)
		}
		if len(name) == 0 {
			return fmt.Errorf("%s did not contain a name", resourceString)
		}

		o.RolesToReconcile = append(o.RolesToReconcile, name)
	}

	return nil
}

func (o *ReconcileClusterRolesOptions) Validate() error {
	if o.RoleClient == nil {
		return errors.New("a role client is required")
	}
	if o.Output != "yaml" && o.Output != "json" && o.Output != "" {
		return fmt.Errorf("unknown output specified: %s", o.Output)
	}
	return nil
}

// RunReconcileClusterRoles contains all the necessary functionality for the OpenShift cli reconcile-cluster-roles command
func (o *ReconcileClusterRolesOptions) RunReconcileClusterRoles(cmd *cobra.Command, f *clientcmd.Factory) error {
	changedClusterRoles, err := o.ChangedClusterRoles()
	if err != nil {
		return err
	}

	if len(changedClusterRoles) == 0 {
		return nil
	}

	if (len(o.Output) != 0) && !o.Confirmed {
		list := &kapi.List{}
		for _, item := range changedClusterRoles {
			list.Items = append(list.Items, item)
		}
		mapper, _ := f.Object(false)
		fn := cmdutil.VersionedPrintObject(f.PrintObject, cmd, mapper, o.Out)
		if err := fn(list); err != nil {
			return err
		}
	}

	if o.Confirmed {
		return o.ReplaceChangedRoles(changedClusterRoles)
	}

	return nil
}

// ChangedClusterRoles returns the roles that must be created and/or updated to
// match the recommended bootstrap policy
func (o *ReconcileClusterRolesOptions) ChangedClusterRoles() ([]*authorizationapi.ClusterRole, error) {
	changedRoles := []*authorizationapi.ClusterRole{}

	rolesToReconcile := sets.NewString(o.RolesToReconcile...)
	rolesNotFound := sets.NewString(o.RolesToReconcile...)
	bootstrapClusterRoles := bootstrappolicy.GetBootstrapClusterRoles()
	for i := range bootstrapClusterRoles {
		expectedClusterRole := &bootstrapClusterRoles[i]
		if (len(rolesToReconcile) > 0) && !rolesToReconcile.Has(expectedClusterRole.Name) {
			continue
		}
		rolesNotFound.Delete(expectedClusterRole.Name)

		actualClusterRole, err := o.RoleClient.Get(expectedClusterRole.Name)
		if kapierrors.IsNotFound(err) {
			changedRoles = append(changedRoles, expectedClusterRole)
			continue
		}
		if err != nil {
			return nil, err
		}

		// Copy any existing labels/annotations, so the displayed update is correct
		// This assumes bootstrap roles will not set any labels/annotations
		// These aren't actually used during update; the latest labels/annotations are pulled from the existing object again
		expectedClusterRole.Labels = actualClusterRole.Labels
		expectedClusterRole.Annotations = actualClusterRole.Annotations

		if !kapi.Semantic.DeepEqual(expectedClusterRole.Rules, actualClusterRole.Rules) {
			if o.Union {
				_, missingRules := rulevalidation.Covers(expectedClusterRole.Rules, actualClusterRole.Rules)
				expectedClusterRole.Rules = append(expectedClusterRole.Rules, missingRules...)
			}
			changedRoles = append(changedRoles, expectedClusterRole)
		}
	}

	if len(rolesNotFound) != 0 {
		// return the known changes and the error so that a caller can decide if he wants a partial update
		return changedRoles, fmt.Errorf("did not find requested cluster role %s", rolesNotFound.List())
	}

	return changedRoles, nil
}

// ReplaceChangedRoles will reconcile all the changed roles back to the recommended bootstrap policy
func (o *ReconcileClusterRolesOptions) ReplaceChangedRoles(changedRoles []*authorizationapi.ClusterRole) error {
	for i := range changedRoles {
		role, err := o.RoleClient.Get(changedRoles[i].Name)
		if err != nil && !kapierrors.IsNotFound(err) {
			return err
		}

		if kapierrors.IsNotFound(err) {
			createdRole, err := o.RoleClient.Create(changedRoles[i])
			if err != nil {
				return err
			}

			fmt.Fprintf(o.Out, "clusterrole/%s\n", createdRole.Name)
			continue
		}

		role.Rules = changedRoles[i].Rules
		updatedRole, err := o.RoleClient.Update(role)
		if err != nil {
			return err
		}

		fmt.Fprintf(o.Out, "clusterrole/%s\n", updatedRole.Name)
	}

	return nil
}
<commit_msg>Omit needless reconciles<commit_after>package policy

import (
	"errors"
	"fmt"
	"io"

	"github.com/spf13/cobra"

	kapi "k8s.io/kubernetes/pkg/api"
	kapierrors "k8s.io/kubernetes/pkg/api/errors"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/util/sets"

	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
	"github.com/openshift/origin/pkg/authorization/rulevalidation"
	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
	cmdutil "github.com/openshift/origin/pkg/cmd/util"
	osutil "github.com/openshift/origin/pkg/cmd/util"
	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
)

// ReconcileClusterRolesRecommendedName is the recommended command name
const ReconcileClusterRolesRecommendedName = "reconcile-cluster-roles"

type ReconcileClusterRolesOptions struct {
	// RolesToReconcile says which roles should be reconciled.  An empty or nil slice means
	// reconcile all of them.
	RolesToReconcile []string

	Confirmed bool
	Union     bool

	Out    io.Writer
	Output string

	RoleClient client.ClusterRoleInterface
}

const (
	reconcileLong = `
Replace cluster roles to match the recommended bootstrap policy

This command will inspect the cluster roles against the recommended bootstrap policy.  Any cluster role
that does not match will be replaced by the recommended bootstrap role.  This command will not remove
any additional cluster role.

You can see which cluster role have recommended changed by choosing an output type.`

	reconcileExample = `  # Display the cluster roles that would be modified
  $ %[1]s

  # Replace cluster roles that don't match the current defaults
  $ %[1]s --confirm

  # Display the union of the default and modified cluster roles
  $ %[1]s --additive-only`
)

// NewCmdReconcileClusterRoles implements the OpenShift cli reconcile-cluster-roles command
func NewCmdReconcileClusterRoles(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	o := &ReconcileClusterRolesOptions{
		Out:   out,
		Union: true,
	}

	cmd := &cobra.Command{
		Use:     name + " [ClusterRoleName]...",
		Short:   "Replace cluster roles to match the recommended bootstrap policy",
		Long:    reconcileLong,
		Example: fmt.Sprintf(reconcileExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			if err := o.Complete(cmd, f, args); err != nil {
				kcmdutil.CheckErr(err)
			}

			if err := o.Validate(); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
			}

			if err := o.RunReconcileClusterRoles(cmd, f); err != nil {
				kcmdutil.CheckErr(err)
			}
		},
	}

	cmd.Flags().BoolVar(&o.Confirmed, "confirm", o.Confirmed, "Specify that cluster roles should be modified. Defaults to false, displaying what would be replaced but not actually replacing anything.")
	cmd.Flags().BoolVar(&o.Union, "additive-only", o.Union, "Preserves modified cluster roles.")
	kcmdutil.AddPrinterFlags(cmd)
	cmd.Flags().Lookup("output").DefValue = "yaml"
	cmd.Flags().Lookup("output").Value.Set("yaml")

	return cmd
}

func (o *ReconcileClusterRolesOptions) Complete(cmd *cobra.Command, f *clientcmd.Factory, args []string) error {
	oclient, _, err := f.Clients()
	if err != nil {
		return err
	}
	o.RoleClient = oclient.ClusterRoles()

	o.Output = kcmdutil.GetFlagString(cmd, "output")

	mapper, _ := f.Object(false)
	for _, resourceString := range args {
		resource, name, err := osutil.ResolveResource(authorizationapi.Resource("clusterroles"), resourceString, mapper)
		if err != nil {
			return err
		}
		if resource != authorizationapi.Resource("clusterroles") {
			return fmt.Errorf("%v is not a valid resource type for this command", resource)
		}
		if len(name) == 0 {
			return fmt.Errorf("%s did not contain a name", resourceString)
		}

		o.RolesToReconcile = append(o.RolesToReconcile, name)
	}

	return nil
}

func (o *ReconcileClusterRolesOptions) Validate() error {
	if o.RoleClient == nil {
		return errors.New("a role client is required")
	}
	if o.Output != "yaml" && o.Output != "json" && o.Output != "" {
		return fmt.Errorf("unknown output specified: %s", o.Output)
	}
	return nil
}

// RunReconcileClusterRoles contains all the necessary functionality for the OpenShift cli reconcile-cluster-roles command
func (o *ReconcileClusterRolesOptions) RunReconcileClusterRoles(cmd *cobra.Command, f *clientcmd.Factory) error {
	changedClusterRoles, err := o.ChangedClusterRoles()
	if err != nil {
		return err
	}

	if len(changedClusterRoles) == 0 {
		return nil
	}

	if (len(o.Output) != 0) && !o.Confirmed {
		list := &kapi.List{}
		for _, item := range changedClusterRoles {
			list.Items = append(list.Items, item)
		}
		mapper, _ := f.Object(false)
		fn := cmdutil.VersionedPrintObject(f.PrintObject, cmd, mapper, o.Out)
		if err := fn(list); err != nil {
			return err
		}
	}

	if o.Confirmed {
		return o.ReplaceChangedRoles(changedClusterRoles)
	}

	return nil
}

// ChangedClusterRoles returns the roles that must be created and/or updated to
// match the recommended bootstrap policy
func (o *ReconcileClusterRolesOptions) ChangedClusterRoles() ([]*authorizationapi.ClusterRole, error) {
	changedRoles := []*authorizationapi.ClusterRole{}

	rolesToReconcile := sets.NewString(o.RolesToReconcile...)
	rolesNotFound := sets.NewString(o.RolesToReconcile...)
	bootstrapClusterRoles := bootstrappolicy.GetBootstrapClusterRoles()
	for i := range bootstrapClusterRoles {
		expectedClusterRole := &bootstrapClusterRoles[i]
		if (len(rolesToReconcile) > 0) && !rolesToReconcile.Has(expectedClusterRole.Name) {
			continue
		}
		rolesNotFound.Delete(expectedClusterRole.Name)

		actualClusterRole, err := o.RoleClient.Get(expectedClusterRole.Name)
		if kapierrors.IsNotFound(err) {
			changedRoles = append(changedRoles, expectedClusterRole)
			continue
		}
		if err != nil {
			return nil, err
		}

		// Copy any existing labels/annotations, so the displayed update is correct
		// This assumes bootstrap roles will not set any labels/annotations
		// These aren't actually used during update; the latest labels/annotations are pulled from the existing object again
		expectedClusterRole.Labels = actualClusterRole.Labels
		expectedClusterRole.Annotations = actualClusterRole.Annotations

		_, extraRules := rulevalidation.Covers(expectedClusterRole.Rules, actualClusterRole.Rules)
		_, missingRules := rulevalidation.Covers(actualClusterRole.Rules, expectedClusterRole.Rules)

		// We need to reconcile:
		// 1. if we're missing rules
		// 2. if there are extra rules we need to remove
		if (len(missingRules) > 0) || (!o.Union && len(extraRules) > 0) {
			if o.Union {
				expectedClusterRole.Rules = append(expectedClusterRole.Rules, extraRules...)
			}
			changedRoles = append(changedRoles, expectedClusterRole)
		}
	}

	if len(rolesNotFound) != 0 {
		// return the known changes and the error so that a caller can decide if he wants a partial update
		return changedRoles, fmt.Errorf("did not find requested cluster role %s", rolesNotFound.List())
	}

	return changedRoles, nil
}

// ReplaceChangedRoles will reconcile all the changed roles back to the recommended bootstrap policy
func (o *ReconcileClusterRolesOptions) ReplaceChangedRoles(changedRoles []*authorizationapi.ClusterRole) error {
	for i := range changedRoles {
		role, err := o.RoleClient.Get(changedRoles[i].Name)
		if err != nil && !kapierrors.IsNotFound(err) {
			return err
		}

		if kapierrors.IsNotFound(err) {
			createdRole, err := o.RoleClient.Create(changedRoles[i])
			if err != nil {
				return err
			}

			fmt.Fprintf(o.Out, "clusterrole/%s\n", createdRole.Name)
			continue
		}

		role.Rules = changedRoles[i].Rules
		updatedRole, err := o.RoleClient.Update(role)
		if err != nil {
			return err
		}

		fmt.Fprintf(o.Out, "clusterrole/%s\n", updatedRole.Name)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 create

import (
	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

var (
	pcLong = templates.LongDesc(i18n.T(`
		Create a priorityclass with the specified name, value, globalDefault and description`))

	pcExample = templates.Examples(i18n.T(`
		# Create a priorityclass named high-priority
		kubectl create priorityclass default-priority --value=1000 --description="high priority"

		# Create a priorityclass named default-priority that considered as the global default priority
		kubectl create priorityclass default-priority --value=1000 --global-default=true --description="default priority"`))
)

type PriorityClassOpts struct {
	CreateSubcommandOptions *CreateSubcommandOptions
}

// NewCmdCreatePriorityClass is a macro command to create a new priorityClass.
func NewCmdCreatePriorityClass(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	options := &PriorityClassOpts{
		CreateSubcommandOptions: NewCreateSubcommandOptions(ioStreams),
	}

	cmd := &cobra.Command{
		Use: "priorityclass NAME --value=VALUE --global-default=BOOL [--dry-run]",
		DisableFlagsInUseLine: true,
		Aliases:               []string{"pc"},
		Short:                 i18n.T("Create a priorityclass with the specified name."),
		Long:                  pcLong,
		Example:               pcExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(options.Complete(f, cmd, args))
			cmdutil.CheckErr(options.Run())
		},
	}

	options.CreateSubcommandOptions.PrintFlags.AddFlags(cmd)

	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddGeneratorFlags(cmd, cmdutil.PriorityClassV1Alpha1GeneratorName)

	cmd.Flags().Int32("value", 0, i18n.T("the value of this priority class."))
	cmd.Flags().Bool("global-default", false, i18n.T("global-default specifies whether this PriorityClass should be considered as the default priority."))
	cmd.Flags().String("description", "", i18n.T("description is an arbitrary string that usually provides guidelines on when this priority class should be used."))
	return cmd
}

func (o *PriorityClassOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	name, err := NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}

	var generator kubectl.StructuredGenerator
	switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
	case cmdutil.PriorityClassV1Alpha1GeneratorName:
		generator = &kubectl.PriorityClassV1Generator{
			Name:          name,
			Value:         cmdutil.GetFlagInt32(cmd, "value"),
			GlobalDefault: cmdutil.GetFlagBool(cmd, "global-default"),
			Description:   cmdutil.GetFlagString(cmd, "description"),
		}
	default:
		return errUnsupportedGenerator(cmd, generatorName)
	}

	return o.CreateSubcommandOptions.Complete(f, cmd, args, generator)
}

// CreatePriorityClass implements the behavior to run the create priorityClass command.
func (o *PriorityClassOpts) Run() error {
	return o.CreateSubcommandOptions.Run()
}
<commit_msg>fix a typo error<commit_after>/*
Copyright 2017 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 create

import (
	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

var (
	pcLong = templates.LongDesc(i18n.T(`
		Create a priorityclass with the specified name, value, globalDefault and description`))

	pcExample = templates.Examples(i18n.T(`
		# Create a priorityclass named high-priority
		kubectl create priorityclass high-priority --value=1000 --description="high priority"

		# Create a priorityclass named default-priority that considered as the global default priority
		kubectl create priorityclass default-priority --value=1000 --global-default=true --description="default priority"`))
)

type PriorityClassOpts struct {
	CreateSubcommandOptions *CreateSubcommandOptions
}

// NewCmdCreatePriorityClass is a macro command to create a new priorityClass.
func NewCmdCreatePriorityClass(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	options := &PriorityClassOpts{
		CreateSubcommandOptions: NewCreateSubcommandOptions(ioStreams),
	}

	cmd := &cobra.Command{
		Use: "priorityclass NAME --value=VALUE --global-default=BOOL [--dry-run]",
		DisableFlagsInUseLine: true,
		Aliases:               []string{"pc"},
		Short:                 i18n.T("Create a priorityclass with the specified name."),
		Long:                  pcLong,
		Example:               pcExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(options.Complete(f, cmd, args))
			cmdutil.CheckErr(options.Run())
		},
	}

	options.CreateSubcommandOptions.PrintFlags.AddFlags(cmd)

	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddGeneratorFlags(cmd, cmdutil.PriorityClassV1Alpha1GeneratorName)

	cmd.Flags().Int32("value", 0, i18n.T("the value of this priority class."))
	cmd.Flags().Bool("global-default", false, i18n.T("global-default specifies whether this PriorityClass should be considered as the default priority."))
	cmd.Flags().String("description", "", i18n.T("description is an arbitrary string that usually provides guidelines on when this priority class should be used."))
	return cmd
}

func (o *PriorityClassOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	name, err := NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}

	var generator kubectl.StructuredGenerator
	switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
	case cmdutil.PriorityClassV1Alpha1GeneratorName:
		generator = &kubectl.PriorityClassV1Generator{
			Name:          name,
			Value:         cmdutil.GetFlagInt32(cmd, "value"),
			GlobalDefault: cmdutil.GetFlagBool(cmd, "global-default"),
			Description:   cmdutil.GetFlagString(cmd, "description"),
		}
	default:
		return errUnsupportedGenerator(cmd, generatorName)
	}

	return o.CreateSubcommandOptions.Complete(f, cmd, args, generator)
}

// CreatePriorityClass implements the behavior to run the create priorityClass command.
func (o *PriorityClassOpts) Run() error {
	return o.CreateSubcommandOptions.Run()
}
<|endoftext|>