<commit_before>package autopilot

import (
	"net"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/lightningnetwork/lnd/lnwire"
)

// DefaultConfTarget is the default confirmation target for autopilot channels.
// TODO(halseth): possibly make dynamic, going aggressive->lax as more channels
// are opened.
const DefaultConfTarget = 3

// Node node is an interface which represents n abstract vertex within the
// channel graph. All nodes should have at least a single edge to/from them
// within the graph.
//
// TODO(roasbeef): combine with routing.ChannelGraphSource
type Node interface {
	// PubKey is the identity public key of the node. This will be used to
	// attempt to target a node for channel opening by the main autopilot
	// agent. The key will be returned in serialized compressed format.
	PubKey() [33]byte

	// Addrs returns a slice of publicly reachable public TCP addresses
	// that the peer is known to be listening on.
	Addrs() []net.Addr

	// ForEachChannel is a higher-order function that will be used to
	// iterate through all edges emanating from/to the target node. For
	// each active channel, this function should be called with the
	// populated ChannelEdge that describes the active channel.
	ForEachChannel(func(ChannelEdge) error) error
}

// Channel is a simple struct which contains relevant details of a particular
// channel within the channel graph. The fields in this struct may be used a
// signals for various AttachmentHeuristic implementations.
type Channel struct {
	// ChanID is the short channel ID for this channel as defined within
	// BOLT-0007.
	ChanID lnwire.ShortChannelID

	// Capacity is the capacity of the channel expressed in satoshis.
	Capacity btcutil.Amount

	// FundedAmt is the amount the local node funded into the target
	// channel.
	//
	// TODO(roasbeef): need this?
	FundedAmt btcutil.Amount

	// Node is the peer that this channel has been established with.
	Node NodeID

	// TODO(roasbeef): also add other traits?
	//  * fee, timelock, etc
}

// ChannelEdge is a struct that holds details concerning a channel, but also
// contains a reference to the Node that this channel connects to as a directed
// edge within the graph. The existence of this reference to the connected node
// will allow callers to traverse the graph in an object-oriented manner.
type ChannelEdge struct {
	// Channel contains the attributes of this channel.
	Channel

	// Peer is the peer that this channel creates an edge to in the channel
	// graph.
	Peer Node
}

// ChannelGraph in an interface that represents a traversable channel graph.
// The autopilot agent will use this interface as its source of graph traits in
// order to make decisions concerning which channels should be opened, and to
// whom.
//
// TODO(roasbeef): abstract??
type ChannelGraph interface {
	// ForEachNode is a higher-order function that should be called once
	// for each connected node within the channel graph. If the passed
	// callback returns an error, then execution should be terminated.
	ForEachNode(func(Node) error) error
}

// NodeScore is a tuple mapping a NodeID to a score indicating the preference
// of opening a channel with it.
type NodeScore struct {
	// NodeID is the serialized compressed pubkey of the node that is being
	// scored.
	NodeID NodeID

	// Score is the score given by the heuristic for opening a channel of
	// the given size to this node.
	Score float64
}

// AttachmentDirective describes a channel attachment proscribed by an
// AttachmentHeuristic. It details to which node a channel should be created
// to, and also the parameters which should be used in the channel creation.
type AttachmentDirective struct {
	// NodeID is the serialized compressed pubkey of the target node for
	// this attachment directive. It can be identified by its public key,
	// and therefore can be used along with a ChannelOpener implementation
	// to execute the directive.
	NodeID NodeID

	// ChanAmt is the size of the channel that should be opened, expressed
	// in satoshis.
	ChanAmt btcutil.Amount

	// Addrs is a list of addresses that the target peer may be reachable
	// at.
	Addrs []net.Addr
}

// AttachmentHeuristic is one of the primary interfaces within this package.
// Implementations of this interface will be used to implement a control system
// which automatically regulates channels of a particular agent, attempting to
// optimize channels opened/closed based on various heuristics.  The purpose of
// the interface is to allow an auto-pilot agent to decide if it needs more
// channels, and if so, which exact channels should be opened.
type AttachmentHeuristic interface {
	// Name returns the name of this heuristic.
	Name() string

	// NodeScores is a method that given the current channel graph and
	// current set of local channels, scores the given nodes according to
	// the preference of opening a channel of the given size with them. The
	// returned channel candidates maps the NodeID to a NodeScore for the
	// node.
	//
	// The returned scores will be in the range [0, 1.0], where 0 indicates
	// no improvement in connectivity if a channel is opened to this node,
	// while 1.0 is the maximum possible improvement in connectivity. The
	// implementation of this interface must return scores in this range to
	// properly allow the autopilot agent to make a reasonable choice based
	// on the score from multiple heuristics.
	//
	// NOTE: A NodeID not found in the returned map is implicitly given a
	// score of 0.
	NodeScores(g ChannelGraph, chans []Channel,
		chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
		map[NodeID]*NodeScore, error)
}

// ScoreSettable is an interface that indicates that the scores returned by the
// heuristic can be mutated by an external caller. The ExternalScoreAttachment
// currently implements this interface, and so should any heuristic that is
// using the ExternalScoreAttachment as a sub-heuristic, or keeps their own
// internal list of mutable scores, to allow access to setting the internal
// scores.
type ScoreSettable interface {
	// SetNodeScores is used to set the internal map from NodeIDs to
	// scores. The passed scores must be in the range [0, 1.0]. The fist
	// parameter is the name of the targeted heuristic, to allow
	// recursively target specific sub-heuristics. The returned boolean
	// indicates whether the targeted heuristic was found.
	SetNodeScores(string, map[NodeID]float64) (bool, error)
}

var (
	// availableHeuristics holds all heuristics possible to combine for use
	// with the autopilot agent.
	availableHeuristics = []AttachmentHeuristic{
		NewPrefAttachment(),
		NewExternalScoreAttachment(),
	}

	// AvailableHeuristics is a map that holds the name of available
	// heuristics to the actual heuristic for easy lookup. It will be
	// filled during init().
	AvailableHeuristics = make(map[string]AttachmentHeuristic)
)

func init() {
	// Fill the map from heuristic names to available heuristics for easy
	// lookup.
	for _, h := range availableHeuristics {
		AvailableHeuristics[h.Name()] = h
	}
}

// ChannelController is a simple interface that allows an auto-pilot agent to
// open a channel within the graph to a target peer, close targeted channels,
// or add/remove funds from existing channels via a splice in/out mechanisms.
type ChannelController interface {
	// OpenChannel opens a channel to a target peer, using at most amt
	// funds. This means that the resulting channel capacity might be
	// slightly less to account for fees. This function should un-block
	// immediately after the funding transaction that marks the channel
	// open has been broadcast.
	OpenChannel(target *btcec.PublicKey, amt btcutil.Amount) error

	// CloseChannel attempts to close out the target channel.
	//
	// TODO(roasbeef): add force option?
	CloseChannel(chanPoint *wire.OutPoint) error

	// SpliceIn attempts to add additional funds to the target channel via
	// a splice in mechanism. The new channel with an updated capacity
	// should be returned.
	SpliceIn(chanPoint *wire.OutPoint, amt btcutil.Amount) (*Channel, error)

	// SpliceOut attempts to remove funds from an existing channels using a
	// splice out mechanism. The removed funds from the channel should be
	// returned to an output under the control of the backing wallet.
	SpliceOut(chanPoint *wire.OutPoint, amt btcutil.Amount) (*Channel, error)
}
<commit_msg>autopilot: introduce NodeMetric interface for arbitrary graph metrics<commit_after>package autopilot

import (
	"net"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/lightningnetwork/lnd/lnwire"
)

// DefaultConfTarget is the default confirmation target for autopilot channels.
// TODO(halseth): possibly make dynamic, going aggressive->lax as more channels
// are opened.
const DefaultConfTarget = 3

// Node node is an interface which represents n abstract vertex within the
// channel graph. All nodes should have at least a single edge to/from them
// within the graph.
//
// TODO(roasbeef): combine with routing.ChannelGraphSource
type Node interface {
	// PubKey is the identity public key of the node. This will be used to
	// attempt to target a node for channel opening by the main autopilot
	// agent. The key will be returned in serialized compressed format.
	PubKey() [33]byte

	// Addrs returns a slice of publicly reachable public TCP addresses
	// that the peer is known to be listening on.
	Addrs() []net.Addr

	// ForEachChannel is a higher-order function that will be used to
	// iterate through all edges emanating from/to the target node. For
	// each active channel, this function should be called with the
	// populated ChannelEdge that describes the active channel.
	ForEachChannel(func(ChannelEdge) error) error
}

// Channel is a simple struct which contains relevant details of a particular
// channel within the channel graph. The fields in this struct may be used a
// signals for various AttachmentHeuristic implementations.
type Channel struct {
	// ChanID is the short channel ID for this channel as defined within
	// BOLT-0007.
	ChanID lnwire.ShortChannelID

	// Capacity is the capacity of the channel expressed in satoshis.
	Capacity btcutil.Amount

	// FundedAmt is the amount the local node funded into the target
	// channel.
	//
	// TODO(roasbeef): need this?
	FundedAmt btcutil.Amount

	// Node is the peer that this channel has been established with.
	Node NodeID

	// TODO(roasbeef): also add other traits?
	//  * fee, timelock, etc
}

// ChannelEdge is a struct that holds details concerning a channel, but also
// contains a reference to the Node that this channel connects to as a directed
// edge within the graph. The existence of this reference to the connected node
// will allow callers to traverse the graph in an object-oriented manner.
type ChannelEdge struct {
	// Channel contains the attributes of this channel.
	Channel

	// Peer is the peer that this channel creates an edge to in the channel
	// graph.
	Peer Node
}

// ChannelGraph in an interface that represents a traversable channel graph.
// The autopilot agent will use this interface as its source of graph traits in
// order to make decisions concerning which channels should be opened, and to
// whom.
//
// TODO(roasbeef): abstract??
type ChannelGraph interface {
	// ForEachNode is a higher-order function that should be called once
	// for each connected node within the channel graph. If the passed
	// callback returns an error, then execution should be terminated.
	ForEachNode(func(Node) error) error
}

// NodeScore is a tuple mapping a NodeID to a score indicating the preference
// of opening a channel with it.
type NodeScore struct {
	// NodeID is the serialized compressed pubkey of the node that is being
	// scored.
	NodeID NodeID

	// Score is the score given by the heuristic for opening a channel of
	// the given size to this node.
	Score float64
}

// AttachmentDirective describes a channel attachment proscribed by an
// AttachmentHeuristic. It details to which node a channel should be created
// to, and also the parameters which should be used in the channel creation.
type AttachmentDirective struct {
	// NodeID is the serialized compressed pubkey of the target node for
	// this attachment directive. It can be identified by its public key,
	// and therefore can be used along with a ChannelOpener implementation
	// to execute the directive.
	NodeID NodeID

	// ChanAmt is the size of the channel that should be opened, expressed
	// in satoshis.
	ChanAmt btcutil.Amount

	// Addrs is a list of addresses that the target peer may be reachable
	// at.
	Addrs []net.Addr
}

// AttachmentHeuristic is one of the primary interfaces within this package.
// Implementations of this interface will be used to implement a control system
// which automatically regulates channels of a particular agent, attempting to
// optimize channels opened/closed based on various heuristics.  The purpose of
// the interface is to allow an auto-pilot agent to decide if it needs more
// channels, and if so, which exact channels should be opened.
type AttachmentHeuristic interface {
	// Name returns the name of this heuristic.
	Name() string

	// NodeScores is a method that given the current channel graph and
	// current set of local channels, scores the given nodes according to
	// the preference of opening a channel of the given size with them. The
	// returned channel candidates maps the NodeID to a NodeScore for the
	// node.
	//
	// The returned scores will be in the range [0, 1.0], where 0 indicates
	// no improvement in connectivity if a channel is opened to this node,
	// while 1.0 is the maximum possible improvement in connectivity. The
	// implementation of this interface must return scores in this range to
	// properly allow the autopilot agent to make a reasonable choice based
	// on the score from multiple heuristics.
	//
	// NOTE: A NodeID not found in the returned map is implicitly given a
	// score of 0.
	NodeScores(g ChannelGraph, chans []Channel,
		chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
		map[NodeID]*NodeScore, error)
}

// NodeMetric is a common interface for all graph metrics that are not
// directly used as autopilot node scores but may be used in compositional
// heuristics or statistical information exposed to users.
type NodeMetric interface {
	// Name returns the unique name of this metric.
	Name() string

	// Refresh refreshes the metric values based on the current graph.
	Refresh(graph ChannelGraph) error

	// GetMetric returns the latest value of this metric. Values in the
	// map are per node and can be in arbitrary domain. If normalize is
	// set to true, then the returned values are normalized to either
	// [0, 1] or [-1, 1] depending on the metric.
	GetMetric(normalize bool) map[NodeID]float64
}

// ScoreSettable is an interface that indicates that the scores returned by the
// heuristic can be mutated by an external caller. The ExternalScoreAttachment
// currently implements this interface, and so should any heuristic that is
// using the ExternalScoreAttachment as a sub-heuristic, or keeps their own
// internal list of mutable scores, to allow access to setting the internal
// scores.
type ScoreSettable interface {
	// SetNodeScores is used to set the internal map from NodeIDs to
	// scores. The passed scores must be in the range [0, 1.0]. The fist
	// parameter is the name of the targeted heuristic, to allow
	// recursively target specific sub-heuristics. The returned boolean
	// indicates whether the targeted heuristic was found.
	SetNodeScores(string, map[NodeID]float64) (bool, error)
}

var (
	// availableHeuristics holds all heuristics possible to combine for use
	// with the autopilot agent.
	availableHeuristics = []AttachmentHeuristic{
		NewPrefAttachment(),
		NewExternalScoreAttachment(),
	}

	// AvailableHeuristics is a map that holds the name of available
	// heuristics to the actual heuristic for easy lookup. It will be
	// filled during init().
	AvailableHeuristics = make(map[string]AttachmentHeuristic)
)

func init() {
	// Fill the map from heuristic names to available heuristics for easy
	// lookup.
	for _, h := range availableHeuristics {
		AvailableHeuristics[h.Name()] = h
	}
}

// ChannelController is a simple interface that allows an auto-pilot agent to
// open a channel within the graph to a target peer, close targeted channels,
// or add/remove funds from existing channels via a splice in/out mechanisms.
type ChannelController interface {
	// OpenChannel opens a channel to a target peer, using at most amt
	// funds. This means that the resulting channel capacity might be
	// slightly less to account for fees. This function should un-block
	// immediately after the funding transaction that marks the channel
	// open has been broadcast.
	OpenChannel(target *btcec.PublicKey, amt btcutil.Amount) error

	// CloseChannel attempts to close out the target channel.
	//
	// TODO(roasbeef): add force option?
	CloseChannel(chanPoint *wire.OutPoint) error

	// SpliceIn attempts to add additional funds to the target channel via
	// a splice in mechanism. The new channel with an updated capacity
	// should be returned.
	SpliceIn(chanPoint *wire.OutPoint, amt btcutil.Amount) (*Channel, error)

	// SpliceOut attempts to remove funds from an existing channels using a
	// splice out mechanism. The removed funds from the channel should be
	// returned to an output under the control of the backing wallet.
	SpliceOut(chanPoint *wire.OutPoint, amt btcutil.Amount) (*Channel, error)
}
<|endoftext|>


<commit_before>package docker

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"launchpad.net/gnuflag"
)

type addNodeToSchedulerCmd struct {
	fs       *gnuflag.FlagSet
	register bool
}

func (addNodeToSchedulerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-node-add",
		Usage: "docker-node-add [param_name=param_value]... [--register]",
		Desc: `Creates or registers a new node in the cluster.
By default, this command will call the configured IaaS to create a new
machine. Every param will be sent to the IaaS implementation.

--register: Registers an existing docker endpoint. The IaaS won't be called.
            Having a address=<docker_api_url> param is mandatory.
`,
		MinArgs: 0,
	}
}

func (a *addNodeToSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	jsonParams := map[string]string{}
	for _, param := range ctx.Args {
		if strings.Contains(param, "=") {
			keyValue := strings.SplitN(param, "=", 2)
			jsonParams[keyValue[0]] = keyValue[1]
		}
	}
	b, err := json.Marshal(jsonParams)
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/docker/node?register=%t", a.register))
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
	if err != nil {
		return err
	}
	_, err = client.Do(req)
	if err != nil {
		result := make(map[string]string)
		json.Unmarshal([]byte(err.Error()), &result)
		fmt.Fprintf(ctx.Stderr, "Error: %s\n\n%s\n", result["error"], result["description"])
		return nil
	}
	ctx.Stdout.Write([]byte("Node successfully registered.\n"))
	return nil
}

func (a *addNodeToSchedulerCmd) Flags() *gnuflag.FlagSet {
	if a.fs == nil {
		a.fs = gnuflag.NewFlagSet("with-flags", gnuflag.ContinueOnError)
		a.fs.BoolVar(&a.register, "register", false, "Register an already created node")
	}
	return a.fs
}

type removeNodeFromSchedulerCmd struct {
	tsuru.ConfirmationCommand
	fs      *gnuflag.FlagSet
	destroy bool
}

func (removeNodeFromSchedulerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-node-remove",
		Usage: "docker-node-remove <address> [--destroy] [-y]",
		Desc: `Removes a node from the cluster.

--destroy: Destroy the machine in the IaaS used to create it, if it exists.
`,
		MinArgs: 1,
	}
}

func (c *removeNodeFromSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	msg := "Are you sure you sure you want to remove \"%s\" from cluster"
	if c.destroy {
		msg += " and DESTROY the machine from IaaS"
	}
	if !c.Confirm(ctx, fmt.Sprintf(msg+"?", ctx.Args[0])) {
		return nil
	}
	params := map[string]string{"address": ctx.Args[0]}
	if c.destroy {
		params["remove_iaas"] = "true"
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	url, err := cmd.GetURL("/docker/node")
	if err != nil {
		return err
	}
	req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(b))
	if err != nil {
		return err
	}
	_, err = client.Do(req)
	if err != nil {
		return err
	}
	ctx.Stdout.Write([]byte("Node successfully removed.\n"))
	return nil
}

func (c *removeNodeFromSchedulerCmd) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.ConfirmationCommand.Flags()
		c.fs.BoolVar(&c.destroy, "destroy", false, "Destroy node from IaaS")
	}
	return c.fs
}

type listNodesInTheSchedulerCmd struct{}

func (listNodesInTheSchedulerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-node-list",
		Usage: "docker-node-list",
		Desc:  "List available nodes in the cluster",
	}
}

func (listNodesInTheSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/node")
	if err != nil {
		return err
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	var result map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&result)
	if err != nil {
		return err
	}
	machineMap := map[string]map[string]interface{}{}
	if result["machines"] != nil {
		machines := result["machines"].([]interface{})
		for _, m := range machines {
			machine := m.(map[string]interface{})
			machineMap[machine["Address"].(string)] = m.(map[string]interface{})
		}
	}
	t := cmd.Table{Headers: cmd.Row([]string{"Address", "IaaS ID", "Status", "Metadata"}), LineSeparator: true}
	var nodes []interface{}
	if result["nodes"] != nil {
		nodes = result["nodes"].([]interface{})
	}
	for _, n := range nodes {
		node := n.(map[string]interface{})
		addr := node["Address"].(string)
		status := node["Status"].(string)
		result := []string{}
		metadataField, _ := node["Metadata"]
		if metadataField != nil {
			metadata := metadataField.(map[string]interface{})
			for key, value := range metadata {
				result = append(result, fmt.Sprintf("%s=%s", key, value.(string)))
			}
		}
		sort.Strings(result)
		m, ok := machineMap[urlToHost(addr)]
		var iaasId string
		if ok {
			iaasId = m["Id"].(string)
		}
		t.AddRow(cmd.Row([]string{addr, iaasId, status, strings.Join(result, "\n")}))
	}
	t.Sort()
	ctx.Stdout.Write(t.Bytes())
	return nil
}

type listHealingHistoryCmd struct {
	fs            *gnuflag.FlagSet
	nodeOnly      bool
	containerOnly bool
}

func (c *listHealingHistoryCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-healing-list",
		Usage: "docker-healing-list [--node] [--container]",
		Desc:  "List healing history for nodes or containers.",
	}
}

func renderHistoryTable(history []healingEvent, filter string, ctx *cmd.Context) {
	fmt.Fprintln(ctx.Stdout, strings.ToUpper(filter[:1])+filter[1:]+":")
	headers := cmd.Row([]string{"Start", "Finish", "Success", "Failing", "Created", "Error"})
	t := cmd.Table{Headers: headers}
	for _, event := range history {
		if event.Action != filter+"-healing" {
			continue
		}
		data := make([]string, 2)
		if filter == "node" {
			data[0] = event.FailingNode.Address
			data[1] = event.CreatedNode.Address
		} else {
			data[0] = event.FailingContainer.ID
			data[1] = event.CreatedContainer.ID
			if len(data[0]) > 10 {
				data[0] = data[0][:10]
			}
			if len(data[1]) > 10 {
				data[1] = data[1][:10]
			}
		}
		t.AddRow(cmd.Row([]string{
			event.StartTime.Local().Format(time.Stamp),
			event.EndTime.Local().Format(time.Stamp),
			fmt.Sprintf("%t", event.Successful),
			data[0],
			data[1],
			event.Error,
		}))
	}
	t.LineSeparator = true
	t.Sort()
	ctx.Stdout.Write(t.Bytes())
}

func (c *listHealingHistoryCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	var filter string
	if c.nodeOnly && !c.containerOnly {
		filter = "node"
	}
	if c.containerOnly && !c.nodeOnly {
		filter = "container"
	}
	url, err := cmd.GetURL(fmt.Sprintf("/docker/healing?filter=%s", filter))
	if err != nil {
		return err
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	var history []healingEvent
	err = json.NewDecoder(resp.Body).Decode(&history)
	if err != nil {
		return err
	}
	if filter != "" {
		renderHistoryTable(history, filter, ctx)
	} else {
		renderHistoryTable(history, "node", ctx)
		renderHistoryTable(history, "container", ctx)
	}
	return nil
}

func (c *listHealingHistoryCmd) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = gnuflag.NewFlagSet("with-flags", gnuflag.ContinueOnError)
		c.fs.BoolVar(&c.nodeOnly, "node", false, "List only healing process started for nodes")
		c.fs.BoolVar(&c.containerOnly, "container", false, "List only healing process started for containers")
	}
	return c.fs
}
<commit_msg>provision/docker: fix error handling in docker-node-add. fixes #901<commit_after>package docker

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"launchpad.net/gnuflag"
)

type addNodeToSchedulerCmd struct {
	fs       *gnuflag.FlagSet
	register bool
}

func (addNodeToSchedulerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-node-add",
		Usage: "docker-node-add [param_name=param_value]... [--register]",
		Desc: `Creates or registers a new node in the cluster.
By default, this command will call the configured IaaS to create a new
machine. Every param will be sent to the IaaS implementation.

--register: Registers an existing docker endpoint. The IaaS won't be called.
            Having a address=<docker_api_url> param is mandatory.
`,
		MinArgs: 0,
	}
}

func (a *addNodeToSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	jsonParams := map[string]string{}
	for _, param := range ctx.Args {
		if strings.Contains(param, "=") {
			keyValue := strings.SplitN(param, "=", 2)
			jsonParams[keyValue[0]] = keyValue[1]
		}
	}
	b, err := json.Marshal(jsonParams)
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/docker/node?register=%t", a.register))
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
	if err != nil {
		return err
	}
	_, err = client.Do(req)
	if err != nil {
		result := make(map[string]string)
		unmarshalErr := json.Unmarshal([]byte(err.Error()), &result)
		if unmarshalErr != nil {
			return err
		}
		fmt.Fprintf(ctx.Stderr, "Error: %s\n\n%s\n", result["error"], result["description"])
		return nil
	}
	ctx.Stdout.Write([]byte("Node successfully registered.\n"))
	return nil
}

func (a *addNodeToSchedulerCmd) Flags() *gnuflag.FlagSet {
	if a.fs == nil {
		a.fs = gnuflag.NewFlagSet("with-flags", gnuflag.ContinueOnError)
		a.fs.BoolVar(&a.register, "register", false, "Register an already created node")
	}
	return a.fs
}

type removeNodeFromSchedulerCmd struct {
	tsuru.ConfirmationCommand
	fs      *gnuflag.FlagSet
	destroy bool
}

func (removeNodeFromSchedulerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-node-remove",
		Usage: "docker-node-remove <address> [--destroy] [-y]",
		Desc: `Removes a node from the cluster.

--destroy: Destroy the machine in the IaaS used to create it, if it exists.
`,
		MinArgs: 1,
	}
}

func (c *removeNodeFromSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	msg := "Are you sure you sure you want to remove \"%s\" from cluster"
	if c.destroy {
		msg += " and DESTROY the machine from IaaS"
	}
	if !c.Confirm(ctx, fmt.Sprintf(msg+"?", ctx.Args[0])) {
		return nil
	}
	params := map[string]string{"address": ctx.Args[0]}
	if c.destroy {
		params["remove_iaas"] = "true"
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	url, err := cmd.GetURL("/docker/node")
	if err != nil {
		return err
	}
	req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(b))
	if err != nil {
		return err
	}
	_, err = client.Do(req)
	if err != nil {
		return err
	}
	ctx.Stdout.Write([]byte("Node successfully removed.\n"))
	return nil
}

func (c *removeNodeFromSchedulerCmd) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.ConfirmationCommand.Flags()
		c.fs.BoolVar(&c.destroy, "destroy", false, "Destroy node from IaaS")
	}
	return c.fs
}

type listNodesInTheSchedulerCmd struct{}

func (listNodesInTheSchedulerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-node-list",
		Usage: "docker-node-list",
		Desc:  "List available nodes in the cluster",
	}
}

func (listNodesInTheSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/node")
	if err != nil {
		return err
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	var result map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&result)
	if err != nil {
		return err
	}
	machineMap := map[string]map[string]interface{}{}
	if result["machines"] != nil {
		machines := result["machines"].([]interface{})
		for _, m := range machines {
			machine := m.(map[string]interface{})
			machineMap[machine["Address"].(string)] = m.(map[string]interface{})
		}
	}
	t := cmd.Table{Headers: cmd.Row([]string{"Address", "IaaS ID", "Status", "Metadata"}), LineSeparator: true}
	var nodes []interface{}
	if result["nodes"] != nil {
		nodes = result["nodes"].([]interface{})
	}
	for _, n := range nodes {
		node := n.(map[string]interface{})
		addr := node["Address"].(string)
		status := node["Status"].(string)
		result := []string{}
		metadataField, _ := node["Metadata"]
		if metadataField != nil {
			metadata := metadataField.(map[string]interface{})
			for key, value := range metadata {
				result = append(result, fmt.Sprintf("%s=%s", key, value.(string)))
			}
		}
		sort.Strings(result)
		m, ok := machineMap[urlToHost(addr)]
		var iaasId string
		if ok {
			iaasId = m["Id"].(string)
		}
		t.AddRow(cmd.Row([]string{addr, iaasId, status, strings.Join(result, "\n")}))
	}
	t.Sort()
	ctx.Stdout.Write(t.Bytes())
	return nil
}

type listHealingHistoryCmd struct {
	fs            *gnuflag.FlagSet
	nodeOnly      bool
	containerOnly bool
}

func (c *listHealingHistoryCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "docker-healing-list",
		Usage: "docker-healing-list [--node] [--container]",
		Desc:  "List healing history for nodes or containers.",
	}
}

func renderHistoryTable(history []healingEvent, filter string, ctx *cmd.Context) {
	fmt.Fprintln(ctx.Stdout, strings.ToUpper(filter[:1])+filter[1:]+":")
	headers := cmd.Row([]string{"Start", "Finish", "Success", "Failing", "Created", "Error"})
	t := cmd.Table{Headers: headers}
	for _, event := range history {
		if event.Action != filter+"-healing" {
			continue
		}
		data := make([]string, 2)
		if filter == "node" {
			data[0] = event.FailingNode.Address
			data[1] = event.CreatedNode.Address
		} else {
			data[0] = event.FailingContainer.ID
			data[1] = event.CreatedContainer.ID
			if len(data[0]) > 10 {
				data[0] = data[0][:10]
			}
			if len(data[1]) > 10 {
				data[1] = data[1][:10]
			}
		}
		t.AddRow(cmd.Row([]string{
			event.StartTime.Local().Format(time.Stamp),
			event.EndTime.Local().Format(time.Stamp),
			fmt.Sprintf("%t", event.Successful),
			data[0],
			data[1],
			event.Error,
		}))
	}
	t.LineSeparator = true
	t.Sort()
	ctx.Stdout.Write(t.Bytes())
}

func (c *listHealingHistoryCmd) Run(ctx *cmd.Context, client *cmd.Client) error {
	var filter string
	if c.nodeOnly && !c.containerOnly {
		filter = "node"
	}
	if c.containerOnly && !c.nodeOnly {
		filter = "container"
	}
	url, err := cmd.GetURL(fmt.Sprintf("/docker/healing?filter=%s", filter))
	if err != nil {
		return err
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	var history []healingEvent
	err = json.NewDecoder(resp.Body).Decode(&history)
	if err != nil {
		return err
	}
	if filter != "" {
		renderHistoryTable(history, filter, ctx)
	} else {
		renderHistoryTable(history, "node", ctx)
		renderHistoryTable(history, "container", ctx)
	}
	return nil
}

func (c *listHealingHistoryCmd) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = gnuflag.NewFlagSet("with-flags", gnuflag.ContinueOnError)
		c.fs.BoolVar(&c.nodeOnly, "node", false, "List only healing process started for nodes")
		c.fs.BoolVar(&c.containerOnly, "container", false, "List only healing process started for containers")
	}
	return c.fs
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/yvasiyarov/newrelic_platform_go"
	"log"
	"net/http"
	// _ "net/http/pprof"
	"os"
)

func main() {

	// for profiling
	// go func() {
	// 	log.Println(http.ListenAndServe("localhost:6060", nil))
	// }()

	var err error
	var verbose bool
	var newrelic_key string
	flag.StringVar(&newrelic_key, "key", "", "Newrelic license key")
	flag.BoolVar(&verbose, "v", false, "Verbose mode")

	flag.Parse()

	if newrelic_key == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	hostname, err := os.Hostname()
	if err != nil {
		log.Fatal(err)
	}

	top := &IOTopCollector{}
	top.Run()

	component := NewDynamicPluginComponent(hostname, "com.github.maciejmrowiec.io_newrelic")

	plugin := newrelic_platform_go.NewNewrelicPlugin("0.0.1", "dfa8fd72df76280f63342a77af576f97023e7f74", 60)
	plugin.AddComponent(component)

	component.AddDynamicMetrica(NewTotalIOPerCommand(top, "io/process/total_io_percentage"))
	component.AddDynamicMetrica(NewReadRatePerCommand(top, "io/process/read_rate"))
	component.AddDynamicMetrica(NewWriteRatePerCommand(top, "io/process/write_rate"))

	plugin.Verbose = verbose
	plugin.Run()
}
<commit_msg>Comment out unused package<commit_after>package main

import (
	"flag"
	"github.com/yvasiyarov/newrelic_platform_go"
	"log"
	// "net/http"
	// _ "net/http/pprof"
	"os"
)

func main() {

	// for profiling
	// go func() {
	// 	log.Println(http.ListenAndServe("localhost:6060", nil))
	// }()

	var err error
	var verbose bool
	var newrelic_key string
	flag.StringVar(&newrelic_key, "key", "", "Newrelic license key")
	flag.BoolVar(&verbose, "v", false, "Verbose mode")

	flag.Parse()

	if newrelic_key == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	hostname, err := os.Hostname()
	if err != nil {
		log.Fatal(err)
	}

	top := &IOTopCollector{}
	top.Run()

	component := NewDynamicPluginComponent(hostname, "com.github.maciejmrowiec.io_newrelic")

	plugin := newrelic_platform_go.NewNewrelicPlugin("0.0.1", "dfa8fd72df76280f63342a77af576f97023e7f74", 60)
	plugin.AddComponent(component)

	component.AddDynamicMetrica(NewTotalIOPerCommand(top, "io/process/total_io_percentage"))
	component.AddDynamicMetrica(NewReadRatePerCommand(top, "io/process/read_rate"))
	component.AddDynamicMetrica(NewWriteRatePerCommand(top, "io/process/write_rate"))

	plugin.Verbose = verbose
	plugin.Run()
}
<|endoftext|>


<commit_before>// handler package implements a library for handling run lambda requests from
// the worker server.
package handler

import (
	"container/list"
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"sync/atomic"
	"time"

	"github.com/open-lambda/open-lambda/worker/config"
	"github.com/open-lambda/open-lambda/worker/handler/state"
	"github.com/open-lambda/open-lambda/worker/import-cache"
	"github.com/open-lambda/open-lambda/worker/registry"

	sb "github.com/open-lambda/open-lambda/worker/sandbox"
)

// HandlerSet represents a collection of Handlers of a worker server. It
// manages the Handler by HandlerLRU.
type HandlerManagerSet struct {
	mutex      sync.Mutex
	hmMap      map[string]*HandlerManager
	regMgr     registry.RegistryManager
	sbFactory  sb.SandboxFactory
	cacheMgr   *cache.CacheManager
	config     *config.Config
	lru        *HandlerLRU
	workerDir  string
	maxRunners int
	hhits      *int64
	ihits      *int64
	misses     *int64
}

type HandlerManager struct {
	name        string
	mutex       sync.Mutex
	hms         *HandlerManagerSet
	handlers    *list.List
	hElements   map[*Handler]*list.Element
	workingDir  string
	maxHandlers int
	lastPull    *time.Time
	code        []byte
	codeDir     string
	pkgs        []string
}

// Handler handles requests to run a lambda on a worker server. It handles
// concurrency and communicates with the sandbox manager to change the
// state of the container that servers the lambda.
type Handler struct {
	name    string
	id      string
	mutex   sync.Mutex
	hm      *HandlerManager
	sandbox sb.Sandbox
	fs      *cache.ForkServer
	hostDir string
	runners int
	usage   int
}

// NewHandlerSet creates an empty HandlerSet
func NewHandlerManagerSet(opts *config.Config) (hms *HandlerManagerSet, err error) {
	rm, err := registry.InitRegistryManager(opts)
	if err != nil {
		return nil, err
	}

	sf, err := sb.InitSandboxFactory(opts)
	if err != nil {
		return nil, err
	}

	cm, err := cache.InitCacheManager(opts)
	if err != nil {
		return nil, err
	}

	var hhits int64 = 0
	var ihits int64 = 0
	var misses int64 = 0
	hms = &HandlerManagerSet{
		hmMap:      make(map[string]*HandlerManager),
		regMgr:     rm,
		sbFactory:  sf,
		cacheMgr:   cm,
		config:     opts,
		workerDir:  opts.Worker_dir,
		maxRunners: opts.Max_runners,
		hhits:      &hhits,
		ihits:      &ihits,
		misses:     &misses,
	}

	hms.lru = NewHandlerLRU(hms, opts.Handler_cache_size) //kb

	/*
		if cm != nil {
			go handlerSet.killOrphans()
		}
	*/

	return hms, nil
}

// Get always returns a Handler, creating one if necessarily.
func (hms *HandlerManagerSet) Get(name string) (h *Handler, err error) {
	defer func(start time.Time) {
		log.Printf("get handler took %v\n", time.Since(start))
	}(time.Now())

	hms.mutex.Lock()

	hm := hms.hmMap[name]

	if hm == nil {
		workingDir := filepath.Join(hms.workerDir, "handlers", name)
		hms.hmMap[name] = &HandlerManager{
			name:       name,
			hms:        hms,
			handlers:   list.New(),
			hElements:  make(map[*Handler]*list.Element),
			workingDir: workingDir,
			pkgs:       []string{},
		}

		hm = hms.hmMap[name]
	}

	// find or create handler
	hm.mutex.Lock()
	if hm.handlers.Front() == nil {
		h = &Handler{
			name:    name,
			hm:      hm,
			runners: 1,
		}
	} else {
		hEle := hm.handlers.Front()
		h = hEle.Value.(*Handler)

		// remove from lru if necessary
		h.mutex.Lock()
		if h.runners == 0 {
			hms.lru.Remove(h)
		}

		h.runners += 1

		if h.hm.hms.maxRunners != 0 && h.runners == h.hm.hms.maxRunners {
			hm.handlers.Remove(hEle)
			delete(hm.hElements, h)
		}
		h.mutex.Unlock()
	}
	// not perfect, but removal from the LRU needs to be atomic
	// with respect to the LRU and the HandlerManager
	hms.mutex.Unlock()

	// get code if needed
	if hm.lastPull == nil {
		codeDir, pkgs, err := hms.regMgr.Pull(hm.name)
		if err != nil {
			return nil, err
		}

		now := time.Now()
		hm.lastPull = &now
		hm.codeDir = codeDir
		hm.pkgs = pkgs
	}
	hm.mutex.Unlock()

	return h, nil
}

/*
func (h *HandlerSet) killOrphans() {
	var toDelete string
	for {
		if toDelete != "" {
			h.mutex.Lock()
			handler := h.handlers[toDelete]
			delete(h.handlers, toDelete)
			h.mutex.Unlock()
			go handler.nuke()
		}
		toDelete = ""
		for _, handler := range h.handlers {
			handler.mutex.Lock()
			if handler.fs != nil && handler.fs.Dead {
				toDelete = handler.name
			}
			time.Sleep(50 * time.Microsecond)
			handler.mutex.Unlock()
		}
	}
}
*/

// Dump prints the name and state of the Handlers currently in the HandlerSet.
func (hms *HandlerManagerSet) Dump() {
	hms.mutex.Lock()
	defer hms.mutex.Unlock()

	log.Printf("HANDLERS:\n")
	for name, hm := range hms.hmMap {
		hm.mutex.Lock()
		log.Printf(" %v: %d", name, hm.maxHandlers)
		for e := hm.handlers.Front(); e != nil; e = e.Next() {
			h := e.Value.(*Handler)
			state, _ := h.sandbox.State()
			log.Printf(" > %v: %v\n", h.id, state.String())
		}
		hm.mutex.Unlock()
	}
}

func (hms *HandlerManagerSet) Cleanup() {
	hms.mutex.Lock()
	defer hms.mutex.Unlock()

	for _, hm := range hms.hmMap {
		for e := hm.handlers.Front(); e != nil; e = e.Next() {
			e.Value.(*Handler).nuke()
		}
	}

	hms.sbFactory.Cleanup()

	if hms.cacheMgr != nil {
		hms.cacheMgr.Cleanup()
	}
}

// must be called with handler lock
func (hm *HandlerManager) AddHandler(h *Handler) {
	hms := hm.hms

	// if we finish first
	// no deadlock can occur here despite taking the locks in the
	// opposite order because hm -> h in Get has no reference
	// in the handler list
	if hms.maxRunners != 0 && h.runners == hms.maxRunners-1 {
		hm.mutex.Lock()
		hm.hElements[h] = hm.handlers.PushFront(h)
		hm.maxHandlers = max(hm.maxHandlers, hm.handlers.Len())
		hm.mutex.Unlock()
	}
}

func (hm *HandlerManager) TryRemoveHandler(h *Handler) error {
	hm.mutex.Lock()
	defer hm.mutex.Unlock()
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// someone has come in and has started running
	if h.runners > 0 {
		return errors.New("concurrent runner entered system")
	}

	// remove reference to handler in HandlerManager
	// this ensures h is the last reference to the Handler
	if hEle := hm.hElements[h]; hEle != nil {
		hm.handlers.Remove(hEle)
		delete(hm.hElements, h)
	}

	return nil
}

// RunStart runs the lambda handled by this Handler. It checks if the code has
// been pulled, sandbox been created, and sandbox been started. The channel of
// the sandbox of this lambda is returned.
func (h *Handler) RunStart() (ch *sb.SandboxChannel, err error) {
	defer func(start time.Time) {
		log.Printf("RunStart took %v\n", time.Since(start))
	}(time.Now())

	h.mutex.Lock()
	defer h.mutex.Unlock()

	hm := h.hm
	hms := h.hm.hms

	// create sandbox if needed
	if h.sandbox == nil {
		hit := false
		if hms.cacheMgr == nil || hms.cacheMgr.Full() {
			sandbox, err := hms.sbFactory.Create(hm.codeDir, hm.workingDir, "")
			if err != nil {
				return nil, err
			}

			h.sandbox = sandbox
			if sbState, err := h.sandbox.State(); err != nil {
				return nil, err
			} else if sbState == state.Stopped {
				if err := h.sandbox.Start(); err != nil {
					return nil, err
				}
			} else if sbState == state.Paused {
				if err := h.sandbox.Unpause(); err != nil {
					return nil, err
				}
			}

			err = h.sandbox.RunServer()
			if err != nil {
				return nil, err
			}
		} else {
			if h.sandbox, h.fs, hit, err = hms.cacheMgr.Provision(hms.sbFactory, hm.codeDir, hm.workingDir, hm.pkgs); err != nil {
				return nil, err
			}

			if hit {
				atomic.AddInt64(hms.ihits, 1)
			} else {
				atomic.AddInt64(hms.misses, 1)
			}
		}

		h.id = h.sandbox.ID()
		h.hostDir = h.sandbox.HostDir()

		sockPath := fmt.Sprintf("%s/ol.sock", h.hostDir)

		// wait up to 20s for server to initialize
		start := time.Now()
		// TODO: make pipe compatible with non-olcontainer
		if olcontainer, ok := h.sandbox.(*sb.OLContainerSandbox); ok {
			// use StdoutPipe of olcontainer to sync with lambda server
			ready := make(chan bool, 1)
			defer close(ready)
			go func() {
				pipeDir := filepath.Join(olcontainer.HostDir(), "server_pipe")
				pipe, err := os.OpenFile(pipeDir, os.O_RDWR, 0777)
				if err != nil {
					log.Fatalf("Cannot open pipe: %v\n", err)
				}
				defer pipe.Close()

				// wait for "ready"
				buf := make([]byte, 5)
				_, err = pipe.Read(buf)
				if err != nil {
					log.Fatalf("Cannot read from stdout of olcontainer: %v\n", err)
				} else if string(buf) != "ready" {
					log.Fatalf("Expect to see `ready` but got %s\n", string(buf))
				}
				ready <- true
			}()

			// wait up to 20s for server to initialize
			timeout := time.NewTimer(20 * time.Second)
			defer timeout.Stop()

			select {
			case <-ready:
				log.Printf("wait for server took %v\n", time.Since(start))
			case <-timeout.C:
				return nil, fmt.Errorf("handler server failed to initialize after 20s")
			}
		} else {
			for ok := true; ok; ok = os.IsNotExist(err) {
				_, err = os.Stat(sockPath)
				if hms.config.Sandbox == "olcontainer" && (hms.cacheMgr == nil || hms.cacheMgr.Full()) {
					time.Sleep(10 * time.Microsecond)
					if err := h.sandbox.RunServer(); err != nil {
						return nil, err
					}
				}
				if time.Since(start).Seconds() > 20 {
					return nil, fmt.Errorf("handler server failed to initialize after 20s")
				}
				time.Sleep(50 * time.Microsecond)
			}
		}

		// we are up so we can add ourselves for reuse
		if hms.maxRunners == 0 || h.runners < hms.maxRunners {
			hm.mutex.Lock()
			hm.hElements[h] = hm.handlers.PushFront(h)
			hm.maxHandlers = max(hm.maxHandlers, hm.handlers.Len())
			hm.mutex.Unlock()
		}

	} else if sbState, _ := h.sandbox.State(); sbState == state.Paused {
		// unpause if paused
		atomic.AddInt64(hms.hhits, 1)
		if err := h.sandbox.Unpause(); err != nil {
			return nil, err
		}
	} else {
		atomic.AddInt64(hms.hhits, 1)
	}

	log.Printf("handler hits: %v, import hits: %v, misses: %v", *hms.hhits, *hms.ihits, *hms.misses)
	if err := h.sandbox.WaitForUnpause(5 * time.Second); err != nil {
		return nil, err
	}
	return h.sandbox.Channel()
}

// RunFinish notifies that a request to run the lambda has completed. If no
// request is being run in its sandbox, sandbox will be paused and the handler
// be added to the HandlerLRU.
func (h *Handler) RunFinish() {
	h.mutex.Lock()

	hm := h.hm
	hms := h.hm.hms

	h.runners -= 1

	// are we the last?
	if h.runners == 0 {
		if err := h.sandbox.Pause(); err != nil {
			// TODO(tyler): better way to handle this?  If
			// we can't pause, the handler gets to keep
			// running for free...
			log.Printf("Could not pause %v: %v!  Error: %v\n", h.name, h.id, err)
		}

		if handlerUsage(h) > hms.lru.soft_limit {
			h.mutex.Unlock()

			// we were potentially the last runner
			// try to remove us from the handler manager
			if err := hm.TryRemoveHandler(h); err == nil {
				// we were the last one so... bye
				go h.nuke()
			}
			return
		}

		hm.AddHandler(h)
		hms.lru.Add(h)
	} else {
		hm.AddHandler(h)
	}

	h.mutex.Unlock()
}

func (h *Handler) nuke() {
	if err := h.sandbox.Unpause(); err != nil {
		log.Printf("failed to unpause sandbox :: %v", err.Error())
	}
	if err := h.sandbox.Stop(); err != nil {
		log.Printf("failed to stop sandbox :: %v", err.Error())
	}
	if err := h.sandbox.Remove(); err != nil {
		log.Printf("failed to remove sandbox :: %v", err.Error())
	}
}

// Sandbox returns the sandbox of this Handler.
func (h *Handler) Sandbox() sb.Sandbox {
	return h.sandbox
}

func max(i1, i2 int) int {
	if i1 < i2 {
		return i2
	}
	return i1
}
<commit_msg>handler: remove unnecessary retry in dead code path of runstart<commit_after>// handler package implements a library for handling run lambda requests from
// the worker server.
package handler

import (
	"container/list"
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"sync/atomic"
	"time"

	"github.com/open-lambda/open-lambda/worker/config"
	"github.com/open-lambda/open-lambda/worker/handler/state"
	"github.com/open-lambda/open-lambda/worker/import-cache"
	"github.com/open-lambda/open-lambda/worker/registry"

	sb "github.com/open-lambda/open-lambda/worker/sandbox"
)

// HandlerSet represents a collection of Handlers of a worker server. It
// manages the Handler by HandlerLRU.
type HandlerManagerSet struct {
	mutex      sync.Mutex
	hmMap      map[string]*HandlerManager
	regMgr     registry.RegistryManager
	sbFactory  sb.SandboxFactory
	cacheMgr   *cache.CacheManager
	config     *config.Config
	lru        *HandlerLRU
	workerDir  string
	maxRunners int
	hhits      *int64
	ihits      *int64
	misses     *int64
}

type HandlerManager struct {
	name        string
	mutex       sync.Mutex
	hms         *HandlerManagerSet
	handlers    *list.List
	hElements   map[*Handler]*list.Element
	workingDir  string
	maxHandlers int
	lastPull    *time.Time
	code        []byte
	codeDir     string
	pkgs        []string
}

// Handler handles requests to run a lambda on a worker server. It handles
// concurrency and communicates with the sandbox manager to change the
// state of the container that servers the lambda.
type Handler struct {
	name    string
	id      string
	mutex   sync.Mutex
	hm      *HandlerManager
	sandbox sb.Sandbox
	fs      *cache.ForkServer
	hostDir string
	runners int
	usage   int
}

// NewHandlerSet creates an empty HandlerSet
func NewHandlerManagerSet(opts *config.Config) (hms *HandlerManagerSet, err error) {
	rm, err := registry.InitRegistryManager(opts)
	if err != nil {
		return nil, err
	}

	sf, err := sb.InitSandboxFactory(opts)
	if err != nil {
		return nil, err
	}

	cm, err := cache.InitCacheManager(opts)
	if err != nil {
		return nil, err
	}

	var hhits int64 = 0
	var ihits int64 = 0
	var misses int64 = 0
	hms = &HandlerManagerSet{
		hmMap:      make(map[string]*HandlerManager),
		regMgr:     rm,
		sbFactory:  sf,
		cacheMgr:   cm,
		config:     opts,
		workerDir:  opts.Worker_dir,
		maxRunners: opts.Max_runners,
		hhits:      &hhits,
		ihits:      &ihits,
		misses:     &misses,
	}

	hms.lru = NewHandlerLRU(hms, opts.Handler_cache_size) //kb

	/*
		if cm != nil {
			go handlerSet.killOrphans()
		}
	*/

	return hms, nil
}

// Get always returns a Handler, creating one if necessarily.
func (hms *HandlerManagerSet) Get(name string) (h *Handler, err error) {
	defer func(start time.Time) {
		log.Printf("get handler took %v\n", time.Since(start))
	}(time.Now())

	hms.mutex.Lock()

	hm := hms.hmMap[name]

	if hm == nil {
		workingDir := filepath.Join(hms.workerDir, "handlers", name)
		hms.hmMap[name] = &HandlerManager{
			name:       name,
			hms:        hms,
			handlers:   list.New(),
			hElements:  make(map[*Handler]*list.Element),
			workingDir: workingDir,
			pkgs:       []string{},
		}

		hm = hms.hmMap[name]
	}

	// find or create handler
	hm.mutex.Lock()
	if hm.handlers.Front() == nil {
		h = &Handler{
			name:    name,
			hm:      hm,
			runners: 1,
		}
	} else {
		hEle := hm.handlers.Front()
		h = hEle.Value.(*Handler)

		// remove from lru if necessary
		h.mutex.Lock()
		if h.runners == 0 {
			hms.lru.Remove(h)
		}

		h.runners += 1

		if h.hm.hms.maxRunners != 0 && h.runners == h.hm.hms.maxRunners {
			hm.handlers.Remove(hEle)
			delete(hm.hElements, h)
		}
		h.mutex.Unlock()
	}
	// not perfect, but removal from the LRU needs to be atomic
	// with respect to the LRU and the HandlerManager
	hms.mutex.Unlock()

	// get code if needed
	if hm.lastPull == nil {
		codeDir, pkgs, err := hms.regMgr.Pull(hm.name)
		if err != nil {
			return nil, err
		}

		now := time.Now()
		hm.lastPull = &now
		hm.codeDir = codeDir
		hm.pkgs = pkgs
	}
	hm.mutex.Unlock()

	return h, nil
}

/*
func (h *HandlerSet) killOrphans() {
	var toDelete string
	for {
		if toDelete != "" {
			h.mutex.Lock()
			handler := h.handlers[toDelete]
			delete(h.handlers, toDelete)
			h.mutex.Unlock()
			go handler.nuke()
		}
		toDelete = ""
		for _, handler := range h.handlers {
			handler.mutex.Lock()
			if handler.fs != nil && handler.fs.Dead {
				toDelete = handler.name
			}
			time.Sleep(50 * time.Microsecond)
			handler.mutex.Unlock()
		}
	}
}
*/

// Dump prints the name and state of the Handlers currently in the HandlerSet.
func (hms *HandlerManagerSet) Dump() {
	hms.mutex.Lock()
	defer hms.mutex.Unlock()

	log.Printf("HANDLERS:\n")
	for name, hm := range hms.hmMap {
		hm.mutex.Lock()
		log.Printf(" %v: %d", name, hm.maxHandlers)
		for e := hm.handlers.Front(); e != nil; e = e.Next() {
			h := e.Value.(*Handler)
			state, _ := h.sandbox.State()
			log.Printf(" > %v: %v\n", h.id, state.String())
		}
		hm.mutex.Unlock()
	}
}

func (hms *HandlerManagerSet) Cleanup() {
	hms.mutex.Lock()
	defer hms.mutex.Unlock()

	for _, hm := range hms.hmMap {
		for e := hm.handlers.Front(); e != nil; e = e.Next() {
			e.Value.(*Handler).nuke()
		}
	}

	hms.sbFactory.Cleanup()

	if hms.cacheMgr != nil {
		hms.cacheMgr.Cleanup()
	}
}

// must be called with handler lock
func (hm *HandlerManager) AddHandler(h *Handler) {
	hms := hm.hms

	// if we finish first
	// no deadlock can occur here despite taking the locks in the
	// opposite order because hm -> h in Get has no reference
	// in the handler list
	if hms.maxRunners != 0 && h.runners == hms.maxRunners-1 {
		hm.mutex.Lock()
		hm.hElements[h] = hm.handlers.PushFront(h)
		hm.maxHandlers = max(hm.maxHandlers, hm.handlers.Len())
		hm.mutex.Unlock()
	}
}

func (hm *HandlerManager) TryRemoveHandler(h *Handler) error {
	hm.mutex.Lock()
	defer hm.mutex.Unlock()
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// someone has come in and has started running
	if h.runners > 0 {
		return errors.New("concurrent runner entered system")
	}

	// remove reference to handler in HandlerManager
	// this ensures h is the last reference to the Handler
	if hEle := hm.hElements[h]; hEle != nil {
		hm.handlers.Remove(hEle)
		delete(hm.hElements, h)
	}

	return nil
}

// RunStart runs the lambda handled by this Handler. It checks if the code has
// been pulled, sandbox been created, and sandbox been started. The channel of
// the sandbox of this lambda is returned.
func (h *Handler) RunStart() (ch *sb.SandboxChannel, err error) {
	defer func(start time.Time) {
		log.Printf("RunStart took %v\n", time.Since(start))
	}(time.Now())

	h.mutex.Lock()
	defer h.mutex.Unlock()

	hm := h.hm
	hms := h.hm.hms

	// create sandbox if needed
	if h.sandbox == nil {
		hit := false
		if hms.cacheMgr == nil || hms.cacheMgr.Full() {
			sandbox, err := hms.sbFactory.Create(hm.codeDir, hm.workingDir, "")
			if err != nil {
				return nil, err
			}

			h.sandbox = sandbox
			if sbState, err := h.sandbox.State(); err != nil {
				return nil, err
			} else if sbState == state.Stopped {
				if err := h.sandbox.Start(); err != nil {
					return nil, err
				}
			} else if sbState == state.Paused {
				if err := h.sandbox.Unpause(); err != nil {
					return nil, err
				}
			}

			err = h.sandbox.RunServer()
			if err != nil {
				return nil, err
			}
		} else {
			if h.sandbox, h.fs, hit, err = hms.cacheMgr.Provision(hms.sbFactory, hm.codeDir, hm.workingDir, hm.pkgs); err != nil {
				return nil, err
			}

			if hit {
				atomic.AddInt64(hms.ihits, 1)
			} else {
				atomic.AddInt64(hms.misses, 1)
			}
		}

		h.id = h.sandbox.ID()
		h.hostDir = h.sandbox.HostDir()

		sockPath := fmt.Sprintf("%s/ol.sock", h.hostDir)

		// wait up to 20s for server to initialize
		start := time.Now()
		// TODO: make pipe compatible with non-olcontainer
		if olcontainer, ok := h.sandbox.(*sb.OLContainerSandbox); ok {
			// use StdoutPipe of olcontainer to sync with lambda server
			ready := make(chan bool, 1)
			defer close(ready)
			go func() {
				pipeDir := filepath.Join(olcontainer.HostDir(), "server_pipe")
				pipe, err := os.OpenFile(pipeDir, os.O_RDWR, 0777)
				if err != nil {
					log.Fatalf("Cannot open pipe: %v\n", err)
				}
				defer pipe.Close()

				// wait for "ready"
				buf := make([]byte, 5)
				_, err = pipe.Read(buf)
				if err != nil {
					log.Fatalf("Cannot read from stdout of olcontainer: %v\n", err)
				} else if string(buf) != "ready" {
					log.Fatalf("Expect to see `ready` but got %s\n", string(buf))
				}
				ready <- true
			}()

			// wait up to 20s for server to initialize
			timeout := time.NewTimer(20 * time.Second)
			defer timeout.Stop()

			select {
			case <-ready:
				log.Printf("wait for server took %v\n", time.Since(start))
			case <-timeout.C:
				return nil, fmt.Errorf("handler server failed to initialize after 20s")
			}
		} else {
			for ok := true; ok; ok = os.IsNotExist(err) {
				_, err = os.Stat(sockPath)
				if time.Since(start).Seconds() > 20 {
					return nil, fmt.Errorf("handler server failed to initialize after 20s")
				}
				time.Sleep(1 * time.Millisecond)
			}
		}

		// we are up so we can add ourselves for reuse
		if hms.maxRunners == 0 || h.runners < hms.maxRunners {
			hm.mutex.Lock()
			hm.hElements[h] = hm.handlers.PushFront(h)
			hm.maxHandlers = max(hm.maxHandlers, hm.handlers.Len())
			hm.mutex.Unlock()
		}

	} else if sbState, _ := h.sandbox.State(); sbState == state.Paused {
		// unpause if paused
		atomic.AddInt64(hms.hhits, 1)
		if err := h.sandbox.Unpause(); err != nil {
			return nil, err
		}
	} else {
		atomic.AddInt64(hms.hhits, 1)
	}

	log.Printf("handler hits: %v, import hits: %v, misses: %v", *hms.hhits, *hms.ihits, *hms.misses)
	if err := h.sandbox.WaitForUnpause(5 * time.Second); err != nil {
		return nil, err
	}
	return h.sandbox.Channel()
}

// RunFinish notifies that a request to run the lambda has completed. If no
// request is being run in its sandbox, sandbox will be paused and the handler
// be added to the HandlerLRU.
func (h *Handler) RunFinish() {
	h.mutex.Lock()

	hm := h.hm
	hms := h.hm.hms

	h.runners -= 1

	// are we the last?
	if h.runners == 0 {
		if err := h.sandbox.Pause(); err != nil {
			// TODO(tyler): better way to handle this?  If
			// we can't pause, the handler gets to keep
			// running for free...
			log.Printf("Could not pause %v: %v!  Error: %v\n", h.name, h.id, err)
		}

		if handlerUsage(h) > hms.lru.soft_limit {
			h.mutex.Unlock()

			// we were potentially the last runner
			// try to remove us from the handler manager
			if err := hm.TryRemoveHandler(h); err == nil {
				// we were the last one so... bye
				go h.nuke()
			}
			return
		}

		hm.AddHandler(h)
		hms.lru.Add(h)
	} else {
		hm.AddHandler(h)
	}

	h.mutex.Unlock()
}

func (h *Handler) nuke() {
	if err := h.sandbox.Unpause(); err != nil {
		log.Printf("failed to unpause sandbox :: %v", err.Error())
	}
	if err := h.sandbox.Stop(); err != nil {
		log.Printf("failed to stop sandbox :: %v", err.Error())
	}
	if err := h.sandbox.Remove(); err != nil {
		log.Printf("failed to remove sandbox :: %v", err.Error())
	}
}

// Sandbox returns the sandbox of this Handler.
func (h *Handler) Sandbox() sb.Sandbox {
	return h.sandbox
}

func max(i1, i2 int) int {
	if i1 < i2 {
		return i2
	}
	return i1
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net"
	"syscall"
)

type IP_Writer struct {
	fd         int
	sockAddr   syscall.Sockaddr
	version    uint8
	dst, src   string
	headerLen  uint16
	ttl        uint8
	protocol   uint8
	identifier uint16
}

func NewIP_Writer(dst string, protocol uint8) (*IP_Writer, error) {
	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
	if err != nil {
		fmt.Println("Write's socket failed")
		return nil, err
	}

	dstIPAddr, err := net.ResolveIPAddr("ip", dst)
	if err != nil {
		//fmt.Println(err)
		return nil, err
	}
	fmt.Println("Full Address: ", dstIPAddr)

	addr := &syscall.SockaddrInet4{
		Port: 20000,
		Addr: [4]byte{
			dstIPAddr.IP[12],
			dstIPAddr.IP[13],
			dstIPAddr.IP[14],
			dstIPAddr.IP[15],
		},
	}

	err = syscall.Connect(fd, addr)
	if err != nil {
		return nil, errors.New("Failed to connect.")
	}

	return &IP_Writer{
		fd:         fd,
		sockAddr:   addr,
		version:    4,
		headerLen:  20,
		dst:        dst,
		src:        "127.0.0.1",
		ttl:        8,
		protocol:   17,
		identifier: 20000,
	}, nil
}

func (ipw *IP_Writer) WriteTo(p []byte) error {
	totalLen := uint16(ipw.headerLen) + uint16(len(p))
	fmt.Println("Total Len: ", totalLen)
	packet := make([]byte, ipw.headerLen)
	packet[0] = (byte)((ipw.version << 4) + (uint8)(ipw.headerLen/4)) // Version, IHL
	packet[1] = 0
	packet[2] = (byte)(totalLen >> 8) // Total Len
	packet[3] = (byte)(totalLen)

	id := ipw.identifier
	packet[4] = byte(id >> 8) // Identification
	packet[5] = byte(id)
	ipw.identifier++

	packet[6] = byte(1 << 6)         // Flags: Don't fragment
	packet[7] = 0                    // Fragment Offset
	packet[8] = (byte)(ipw.ttl)      // Time to Live
	packet[9] = (byte)(ipw.protocol) // Protocol

	// Src and Dst IPs
	srcIP := net.ParseIP(ipw.src)
	fmt.Println(srcIP)
	//    fmt.Println(srcIP[12])
	//    fmt.Println(srcIP[13])
	//    fmt.Println(srcIP[14])
	//    fmt.Println(srcIP[15])
	dstIP := net.ParseIP(ipw.dst)
	fmt.Println(dstIP)
	packet[12] = srcIP[12]
	packet[13] = srcIP[13]
	packet[14] = srcIP[14]
	packet[15] = srcIP[15]
	packet[16] = dstIP[12]
	packet[17] = dstIP[13]
	packet[18] = dstIP[14]
	packet[19] = dstIP[15]

	// IPv4 header test (before checksum)
	fmt.Println("Packet before checksum: ", packet)

	// Checksum
	checksum := calcChecksum(packet[:20], true)
	packet[10] = byte(checksum >> 8)
	packet[11] = byte(checksum)

	// Payload
	packet = append(packet, p...)
	fmt.Println("Full Packet:  ", packet)

    // TODO: Allow IP fragmentation (use 1500 as MTU)
	return syscall.Sendto(ipw.fd, packet, 0, ipw.sockAddr)
}

func (ipw *IP_Writer) Close() error {
	return syscall.Close(ipw.fd)
}

/* h := &ipv4.Header{
	Version:  ipv4.Version,      // protocol version
	Len:      20,                // header length
	TOS:      0,                 // type-of-service (0 is everything normal)
	TotalLen: len(x) + 20,       // packet total length (octets)
	ID:       0,                 // identification
	Flags:    ipv4.DontFragment, // flags
	FragOff:  0,                 // fragment offset
	TTL:      8,                 // time-to-live (maximum lifespan in seconds)
	Protocol: 17,                // next protocol (17 is UDP)
	Checksum: 0,                 // checksum (apparently autocomputed)
	//Src:    net.IPv4(127, 0, 0, 1), // source address, apparently done automatically
	Dst: net.ParseIP(c.manager.ipAddress), // destination address
	//Options                         // options, extension headers
}
*/
<commit_msg>Start of branching<commit_after>package main

import (
	"errors"
	"fmt"
	"net"
	"syscall"
)

type IP_Writer struct {
	fd         int
	sockAddr   syscall.Sockaddr
	version    uint8
	dst, src   string
	headerLen  uint16
	ttl        uint8
	protocol   uint8
	identifier uint16
}

func NewIP_Writer(dst string, protocol uint8) (*IP_Writer, error) {
	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
	if err != nil {
		fmt.Println("Write's socket failed")
		return nil, err
	}

	dstIPAddr, err := net.ResolveIPAddr("ip", dst)
	if err != nil {
		//fmt.Println(err)
		return nil, err
	}
	fmt.Println("Full Address: ", dstIPAddr)

	addr := &syscall.SockaddrInet4{
		Port: 20000,
		Addr: [4]byte{
			dstIPAddr.IP[12],
			dstIPAddr.IP[13],
			dstIPAddr.IP[14],
			dstIPAddr.IP[15],
		},
	}

	err = syscall.Connect(fd, addr)
	if err != nil {
		return nil, errors.New("Failed to connect.")
	}

	return &IP_Writer{
		fd:         fd,
		sockAddr:   addr,
		version:    4,
		headerLen:  20,
		dst:        dst,
		src:        "127.0.0.1",
		ttl:        8,
		protocol:   17,
		identifier: 20000,
	}, nil
}

func (ipw *IP_Writer) WriteTo(p []byte) error {
	pSlice := make([][]byte, len(p)/1480+1)
	for i, _ := range pSlice {
		if len(p) <= 1480 * (i + 1) {
			pSlice[i] := p[1480*i : 1480*(i+1)]
		} else {
			pSlice[i] := p[1480*i:]
		}
	}

	for index
	totalLen := uint16(ipw.headerLen) + uint16(len(p))
	fmt.Println("Total Len: ", totalLen)
	packet := make([]byte, ipw.headerLen)
	packet[0] = (byte)((ipw.version << 4) + (uint8)(ipw.headerLen/4)) // Version, IHL
	packet[1] = 0
	packet[2] = (byte)(totalLen >> 8) // Total Len
	packet[3] = (byte)(totalLen)

	id := ipw.identifier
	packet[4] = byte(id >> 8) // Identification
	packet[5] = byte(id)
	ipw.identifier++

	packet[6] = byte(1 << 6)         // Flags: Don't fragment
	packet[7] = 0                    // Fragment Offset
	packet[8] = (byte)(ipw.ttl)      // Time to Live
	packet[9] = (byte)(ipw.protocol) // Protocol

	// Src and Dst IPs
	srcIP := net.ParseIP(ipw.src)
	fmt.Println(srcIP)
	//    fmt.Println(srcIP[12])
	//    fmt.Println(srcIP[13])
	//    fmt.Println(srcIP[14])
	//    fmt.Println(srcIP[15])
	dstIP := net.ParseIP(ipw.dst)
	fmt.Println(dstIP)
	packet[12] = srcIP[12]
	packet[13] = srcIP[13]
	packet[14] = srcIP[14]
	packet[15] = srcIP[15]
	packet[16] = dstIP[12]
	packet[17] = dstIP[13]
	packet[18] = dstIP[14]
	packet[19] = dstIP[15]

	// IPv4 header test (before checksum)
	fmt.Println("Packet before checksum: ", packet)

	// Checksum
	checksum := calcChecksum(packet[:20], true)
	packet[10] = byte(checksum >> 8)
	packet[11] = byte(checksum)

	// Payload
	packet = append(packet, p...)
	fmt.Println("Full Packet:  ", packet)

	// TODO: Allow IP fragmentation (use 1500 as MTU)
	return syscall.Sendto(ipw.fd, packet, 0, ipw.sockAddr)
}

func (ipw *IP_Writer) Close() error {
	return syscall.Close(ipw.fd)
}

/* h := &ipv4.Header{
	Version:  ipv4.Version,      // protocol version
	Len:      20,                // header length
	TOS:      0,                 // type-of-service (0 is everything normal)
	TotalLen: len(x) + 20,       // packet total length (octets)
	ID:       0,                 // identification
	Flags:    ipv4.DontFragment, // flags
	FragOff:  0,                 // fragment offset
	TTL:      8,                 // time-to-live (maximum lifespan in seconds)
	Protocol: 17,                // next protocol (17 is UDP)
	Checksum: 0,                 // checksum (apparently autocomputed)
	//Src:    net.IPv4(127, 0, 0, 1), // source address, apparently done automatically
	Dst: net.ParseIP(c.manager.ipAddress), // destination address
	//Options                         // options, extension headers
}
*/
<|endoftext|>


<commit_before>package backend
<commit_msg>'direct' is renamed to 'local'<commit_after><|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"io/ioutil"
	"os"
	"testing"
	"time"
)

type DummyWatched struct {
	name string
}

func (d *DummyWatched) Name() string {
	return d.name
}

func (d *DummyWatched) Reload() {
	// noop
}

func TestConfigLoading(t *testing.T) {
	editor := GetEditor()
	editor.loadSetting(NewPacket("testdata/Default.sublime-settings"))

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size")
	}

	tab_size := editor.Settings().Get("tab_size").(float64)
	if tab_size != 4 {
		t.Errorf("Expected tab_size to equal 4, got: %v", tab_size)
	}
}

func TestWatch(t *testing.T) {
	editor := GetEditor()
	observedFile := &DummyWatched{"editor_test.go"}
	editor.Watch(observedFile)

	if editor.watchedFiles["editor_test.go"] != observedFile {
		t.Fatal("Expected editor to watch the specified file")
	}
}

func TestWatchOnSaveAs(t *testing.T) {
	var testfile string = "testdata/Default.sublime-settings"
	tests := []struct {
		as string
	}{
		{
			"User.sublime-settings",
		},
		{
			"testdata/User.sublime-settings",
		},
	}
	ed := GetEditor()
	w := ed.NewWindow()
	for i, test := range tests {
		v := w.OpenFile(testfile, 0)
		if err := v.SaveAs(test.as); err != nil {
			t.Fatalf("Test %d: Can't save to `%s`: %s", i, test.as, err)
		}
		if _, exist := ed.watchedFiles[test.as]; !exist {
			t.Errorf("Test %d: Should watch %s file", i, test.as)
		}
		if err := os.Remove(test.as); err != nil {
			t.Errorf("Test %d: Couldn't remove test file %s", i, test.as)
		}
	}
}

func TestWatchingSettings(t *testing.T) {
	// TODO: This won't pass until the settings hierarchy is set up properly.
	return

	var path string = "testdata/Default.sublime-settings"
	editor := GetEditor()
	editor.loadSetting(NewPacket(path))

	buf, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal("Error in reading the default settings")
	}

	data := []byte("{\n\t\"tab_size\": 8\n}")
	err = ioutil.WriteFile(path, data, 0644)
	if err != nil {
		t.Fatal("Error in writing to setting")
	}
	time.Sleep(time.Millisecond * 10)
	if tab_size := editor.Settings().Get("tab_size").(float64); tab_size != 8 {
		t.Errorf("Expected tab_size equal to 8, but got %v", tab_size)
	}

	err = ioutil.WriteFile(path, buf, 0644)
	if err != nil {
		t.Fatal("Error in writing the default back to setting")
	}
}

func TestNewWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())
	_ = ed.NewWindow()

	if len(ed.Windows()) != l+1 {
		t.Errorf("Expected 1 window, but got %d", len(ed.Windows()))
	}
}

func TestRemoveWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())

	w := ed.NewWindow()
	ed.remove(w)

	if len(ed.Windows()) != l {
		t.Errorf("Expected the window to be removed, but %d still remain", len(ed.Windows()))
	}
}

func TestSetFrontend(t *testing.T) {
	f := DummyFrontend{}

	ed := GetEditor()
	ed.SetFrontend(&f)

	if ed.Frontend() != &f {
		t.Errorf("Expected a DummyFrontend to be set, but got %T", ed.Frontend())
	}
}

func TestClipboard(t *testing.T) {
	ed := GetEditor()
	s := "test"

	ed.SetClipboard(s)

	if ed.GetClipboard() != s {
		t.Errorf("Expected %s to be on the clipboard, but got %s", s, ed.GetClipboard())
	}
}
<commit_msg>Add some editor tests.<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"io/ioutil"
	"os"
	"testing"
	"time"
)

type DummyWatched struct {
	name string
}

func (d *DummyWatched) Name() string {
	return d.name
}

func (d *DummyWatched) Reload() {
	// noop
}

func TestGetEditor(t *testing.T) {
	e := GetEditor()
	if e == nil {
		t.Error("Expected an editor, but got nil")
	}
}

func TestLoadKeybinding(t *testing.T) {
	var kb KeyBindings

	editor := GetEditor()
	editor.loadKeybinding(NewPacket("testdata/Default.sublime-keymap"))

	editor.Keybindings().filter(69, &kb)
	if kb.Len() == 69 {
		t.Errorf("Expected editor to have key %d bound, but it didn't", 69)
	}
}

func TestLoadKeybindings(t *testing.T) {
	editor := GetEditor()
	editor.loadKeybindings()

	editor.Keybindings().Len()
	if editor.Keybindings().Len() <= 0 {
		t.Errorf("Expected editor to have some keys bound, but it didn't")
	}
}

func TestLoadSetting(t *testing.T) {
	editor := GetEditor()
	editor.loadSetting(NewPacket("testdata/Default.sublime-settings"))

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}

	tab_size := editor.Settings().Get("tab_size").(float64)
	if tab_size != 4 {
		t.Errorf("Expected tab_size to equal 4, got: %v", tab_size)
	}
}

func TestLoadSettings(t *testing.T) {
	editor := GetEditor()
	editor.loadSettings()

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}
}

func TestInit(t *testing.T) {
	editor := GetEditor()
	editor.Init()

	editor.Keybindings().Len()
	if editor.Keybindings().Len() <= 0 {
		t.Errorf("Expected editor to have some keys bound, but it didn't")
	}

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}
}

func TestWatch(t *testing.T) {
	editor := GetEditor()
	observedFile := &DummyWatched{"editor_test.go"}
	editor.Watch(observedFile)

	if editor.watchedFiles["editor_test.go"] != observedFile {
		t.Fatal("Expected editor to watch the specified file")
	}
}

func TestWatchOnSaveAs(t *testing.T) {
	var testfile string = "testdata/Default.sublime-settings"
	tests := []struct {
		as string
	}{
		{
			"User.sublime-settings",
		},
		{
			"testdata/User.sublime-settings",
		},
	}
	ed := GetEditor()
	w := ed.NewWindow()
	for i, test := range tests {
		v := w.OpenFile(testfile, 0)
		if err := v.SaveAs(test.as); err != nil {
			t.Fatalf("Test %d: Can't save to `%s`: %s", i, test.as, err)
		}
		if _, exist := ed.watchedFiles[test.as]; !exist {
			t.Errorf("Test %d: Should watch %s file", i, test.as)
		}
		if err := os.Remove(test.as); err != nil {
			t.Errorf("Test %d: Couldn't remove test file %s", i, test.as)
		}
	}
}

func TestWatchingSettings(t *testing.T) {
	// TODO: This won't pass until the settings hierarchy is set up properly.
	return

	var path string = "testdata/Default.sublime-settings"
	editor := GetEditor()
	editor.loadSetting(NewPacket(path))

	buf, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal("Error in reading the default settings")
	}

	data := []byte("{\n\t\"tab_size\": 8\n}")
	err = ioutil.WriteFile(path, data, 0644)
	if err != nil {
		t.Fatal("Error in writing to setting")
	}
	time.Sleep(time.Millisecond * 10)
	if tab_size := editor.Settings().Get("tab_size").(float64); tab_size != 8 {
		t.Errorf("Expected tab_size equal to 8, but got %v", tab_size)
	}

	err = ioutil.WriteFile(path, buf, 0644)
	if err != nil {
		t.Fatal("Error in writing the default back to setting")
	}
}

func TestNewWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())
	_ = ed.NewWindow()

	if len(ed.Windows()) != l+1 {
		t.Errorf("Expected 1 window, but got %d", len(ed.Windows()))
	}
}

func TestRemoveWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())

	w := ed.NewWindow()
	ed.remove(w)

	if len(ed.Windows()) != l {
		t.Errorf("Expected the window to be removed, but %d still remain", len(ed.Windows()))
	}
}

func TestSetActiveWindow(t *testing.T) {
	// FIXME: The consecutive calls of Editor.NewWindow cause the tests to hang.
	return

	ed := GetEditor()

	w1 := ed.NewWindow()
	w2 := ed.NewWindow()

	if ed.ActiveWindow() != w2 {
		t.Error("Expected the newest window to be active, but it wasn't")
	}

	ed.SetActiveWindow(w1)

	if ed.ActiveWindow() == w1 {
		t.Error("Expected the first window to be active, but it wasn't")
	}
}

func TestSetFrontend(t *testing.T) {
	f := DummyFrontend{}

	ed := GetEditor()
	ed.SetFrontend(&f)

	if ed.Frontend() != &f {
		t.Errorf("Expected a DummyFrontend to be set, but got %T", ed.Frontend())
	}
}

func TestClipboard(t *testing.T) {
	ed := GetEditor()
	s := "test"

	ed.SetClipboard(s)

	if ed.GetClipboard() != s {
		t.Errorf("Expected %s to be on the clipboard, but got %s", s, ed.GetClipboard())
	}
}
<|endoftext|>


<commit_before>package file

import (
	"bufio"
	"os"
	"path/filepath"
	"strings"

	"github.com/sirupsen/logrus"
)

type SearchResult struct {
	Line int
	Text string
}

func (m *manager) Search(query string) (interface{}, error) {
	//TODO query to pattern(regexp)
	result := map[string]([]SearchResult){}
	err := filepath.Walk(m.basepath, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() && info.Name()[0] == '.' {
			logrus.Debug(info.Name())
			return filepath.SkipDir
		}
		if info.IsDir() {
			return nil
		}
		if info.Name()[0] == '.' {
			return nil
		}

		res, err := fileSearch(path, query)
		if err != nil {
			return err
		}
		if len(res) != 0 {
			result[path[len(m.basepath):]] = res
		}
		return nil
	})
	return result, err
}
func fileSearch(path, query string) ([]SearchResult, error) {
	result := []SearchResult{}
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	linenum := 0

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		linenum++
		if strings.Contains(scanner.Text(), query) {
			result = append(result, SearchResult{
				Line: linenum,
				Text: scanner.Text(),
			})
		}
	}

	return result, scanner.Err()
}
<commit_msg>Ingore binary file when searching<commit_after>package file

import (
	"bufio"
	"os"
	"path/filepath"
	"strings"

	"github.com/sirupsen/logrus"
)

type SearchResult struct {
	Line int
	Text string
}

func (m *manager) Search(query string) (interface{}, error) {
	//TODO query to pattern(regexp)
	result := map[string]([]SearchResult){}
	err := filepath.Walk(m.basepath, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() && info.Name()[0] == '.' {
			logrus.Debug(info.Name())
			return filepath.SkipDir
		}
		if info.IsDir() {
			return nil
		}
		if info.Name()[0] == '.' {
			return nil
		}
		switch filepath.Ext(info.Name()) {
		case ".pdf", ".mp4", ".mp3":
			// if binary
			return nil
		}

		res, err := fileSearch(path, query)
		if err != nil {
			return err
		}
		if len(res) != 0 {
			result[path[len(m.basepath):]] = res
		}
		return nil
	})
	return result, err
}
func fileSearch(path, query string) ([]SearchResult, error) {
	result := []SearchResult{}
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	linenum := 0

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		linenum++
		if strings.Contains(scanner.Text(), query) {
			result = append(result, SearchResult{
				Line: linenum,
				Text: scanner.Text(),
			})
		}
	}

	return result, scanner.Err()
}
<|endoftext|>


<commit_before>package media

type Media struct {
	ID      int    `json:"id"`
	Name    string `json:"name"`
	Config  MediaConfig `json:"config"`
	Plugins []MediaPlugins `json:"plugins"`
}

type MediaConfig struct {
	Styles []string `json:"styles"`
}

type MediaPlugins struct {
	Name       string `json:"name"`
	EltName    string `json:"eltName"`
	Files      []string `json:"files"`
	PropValues MediaPluginProps `json:"propValues"`
}

type MediaPluginProps struct {
	X map[string]interface{} `json:"-"`
}
<commit_msg>[Add] Description's comments<commit_after>package media

/**
The global attributes for a Media
 */
type Media struct {
	ID      int    `json:"id"`
	Name    string `json:"name"`
	Config  MediaConfig `json:"config"`
	Plugins []MediaPlugin `json:"plugins"`
}

type MediaConfig struct {
	Styles []string `json:"styles"`
}

/**
Properties and configuration for a plugin used in the media
 */
type MediaPlugin struct {
	Name       string `json:"name"`
	EltName    string `json:"eltName"`
	Files      []string `json:"files"`
	PropValues MediaPluginProps `json:"propValues"`
}

/**
Because we don't know what will compounds the props for a plugin, we use a map[string] interface{}
 */
type MediaPluginProps struct {
	X map[string]interface{} `json:"-"`
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 backup

import (
	"fmt"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/sys"
)

////////////////////////////////////////////////////////////////////////
// Public
////////////////////////////////////////////////////////////////////////

// An object that knows how to restore previously backed up directories.
type DirectoryRestorer interface {
	// Recursively restore a directory based on the listing named by the supplied
	// score. The first call should set basePath to the target directory and
	// relPath to the empty string. The target directory must already exist.
	RestoreDirectory(score blob.Score, basePath, relPath string) (err error)
}

// Create a directory restorer that uses the supplied objects.
func NewDirectoryRestorer(
	blobStore blob.Store,
	fileSystem fs.FileSystem,
	fileRestorer FileRestorer,
) (restorer DirectoryRestorer, err error) {
	userRegistry, err := sys.NewUserRegistry()
	if err != nil {
		err = fmt.Errorf("NewUserRegistry: %v", err)
	}

	groupRegistry, err := sys.NewGroupRegistry()
	if err != nil {
		err = fmt.Errorf("NewGroupRegistry: %v", err)
	}

	createRestorer := func(wrapped DirectoryRestorer) DirectoryRestorer {
		restorer, err := NewNonRecursiveDirectoryRestorer(
			blobStore,
			fileSystem,
			fileRestorer,
			userRegistry,
			groupRegistry,
			wrapped,
		)

		if err != nil {
			panic(err)
		}

		return restorer
	}

	return &onDemandDirRestorer{createRestorer}, nil
}

////////////////////////////////////////////////////////////////////////
// Implementation details
////////////////////////////////////////////////////////////////////////

// A directory restorer that creates a new directory restorer for each call.
// This breaks a self-dependency that would be needed to make use of
// NewNonRecursiveDirectoryRestorer.
type onDemandDirRestorer struct {
	createRestorer func(wrapped DirectoryRestorer) DirectoryRestorer
}

func (r *onDemandDirRestorer) RestoreDirectory(
	score blob.Score,
	basePath string,
	relPath string,
) (err error) {
	return r.createRestorer(r).RestoreDirectory(score, basePath, relPath)
}

// Split out for testability. You should not use this directly.
func NewNonRecursiveDirectoryRestorer(
	blobStore blob.Store,
	fileSystem fs.FileSystem,
	fileRestorer FileRestorer,
	userRegistry sys.UserRegistry,
	groupRegistry sys.GroupRegistry,
	wrapped DirectoryRestorer,
) (restorer DirectoryRestorer, err error) {
	restorer = &dirRestorer{
		blobStore,
		fileSystem,
		fileRestorer,
		userRegistry,
		groupRegistry,
		wrapped,
	}

	return
}

type dirRestorer struct {
	blobStore    blob.Store
	fileSystem   fs.FileSystem
	fileRestorer FileRestorer
	userRegistry sys.UserRegistry
	groupRegistry sys.GroupRegistry
	wrapped      DirectoryRestorer
}

func (r *dirRestorer) RestoreDirectory(
	score blob.Score,
	basePath string,
	relPath string,
) (err error) {
	err = fmt.Errorf("TODO")
	return
}
<commit_msg>Added helper methods.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 backup

import (
	"fmt"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/sys"
)

////////////////////////////////////////////////////////////////////////
// Public
////////////////////////////////////////////////////////////////////////

// An object that knows how to restore previously backed up directories.
type DirectoryRestorer interface {
	// Recursively restore a directory based on the listing named by the supplied
	// score. The first call should set basePath to the target directory and
	// relPath to the empty string. The target directory must already exist.
	RestoreDirectory(score blob.Score, basePath, relPath string) (err error)
}

// Create a directory restorer that uses the supplied objects.
func NewDirectoryRestorer(
	blobStore blob.Store,
	fileSystem fs.FileSystem,
	fileRestorer FileRestorer,
) (restorer DirectoryRestorer, err error) {
	userRegistry, err := sys.NewUserRegistry()
	if err != nil {
		err = fmt.Errorf("NewUserRegistry: %v", err)
	}

	groupRegistry, err := sys.NewGroupRegistry()
	if err != nil {
		err = fmt.Errorf("NewGroupRegistry: %v", err)
	}

	createRestorer := func(wrapped DirectoryRestorer) DirectoryRestorer {
		restorer, err := NewNonRecursiveDirectoryRestorer(
			blobStore,
			fileSystem,
			fileRestorer,
			userRegistry,
			groupRegistry,
			wrapped,
		)

		if err != nil {
			panic(err)
		}

		return restorer
	}

	return &onDemandDirRestorer{createRestorer}, nil
}

////////////////////////////////////////////////////////////////////////
// Implementation details
////////////////////////////////////////////////////////////////////////

// A directory restorer that creates a new directory restorer for each call.
// This breaks a self-dependency that would be needed to make use of
// NewNonRecursiveDirectoryRestorer.
type onDemandDirRestorer struct {
	createRestorer func(wrapped DirectoryRestorer) DirectoryRestorer
}

func (r *onDemandDirRestorer) RestoreDirectory(
	score blob.Score,
	basePath string,
	relPath string,
) (err error) {
	return r.createRestorer(r).RestoreDirectory(score, basePath, relPath)
}

// Split out for testability. You should not use this directly.
func NewNonRecursiveDirectoryRestorer(
	blobStore blob.Store,
	fileSystem fs.FileSystem,
	fileRestorer FileRestorer,
	userRegistry sys.UserRegistry,
	groupRegistry sys.GroupRegistry,
	wrapped DirectoryRestorer,
) (restorer DirectoryRestorer, err error) {
	restorer = &dirRestorer{
		blobStore,
		fileSystem,
		fileRestorer,
		userRegistry,
		groupRegistry,
		wrapped,
	}

	return
}

type dirRestorer struct {
	blobStore    blob.Store
	fileSystem   fs.FileSystem
	fileRestorer FileRestorer
	userRegistry sys.UserRegistry
	groupRegistry sys.GroupRegistry
	wrapped      DirectoryRestorer
}

func (r *dirRestorer) RestoreDirectory(
	score blob.Score,
	basePath string,
	relPath string,
) (err error) {
	err = fmt.Errorf("TODO")
	return
}

func (r *dirRestorer) chooseUserId(
	uid sys.UserId,
	username *string,
) (sys.UserId, error) {
	// If there is no symbolic username, just return the UID.
	if username == nil {
		return uid, nil
	}

	// Attempt to look up the username. If it's not found, return the UID.
	betterUid, err := r.userRegistry.FindByName(*username)

	if _, ok := err.(sys.NotFoundError); ok {
		return uid, nil
	} else if err != nil {
		return 0, fmt.Errorf("userRegistry.FindByName: %v", err)
	}

	return betterUid, nil
}

func (r *dirRestorer) chooseGroupId(
	gid sys.GroupId,
	groupname *string,
) (sys.GroupId, error) {
	// If there is no symbolic groupname, just return the UID.
	if groupname == nil {
		return gid, nil
	}

	// Attempt to look up the groupname. If it's not found, return the UID.
	betterGid, err := r.groupRegistry.FindByName(*groupname)

	if _, ok := err.(sys.NotFoundError); ok {
		return gid, nil
	} else if err != nil {
		return 0, fmt.Errorf("groupRegistry.FindByName: %v", err)
	}

	return betterGid, nil
}
<|endoftext|>


<commit_before>package irc

const (
	RPL_WELCOME           = "001"
	RPL_YOURHOST          = "002"
	RPL_CREATED           = "003"
	RPL_MYINFO            = "004"
	RPL_BOUNCE            = "005"
	RPL_TRACELINK         = "200"
	RPL_TRACECONNECTING   = "201"
	RPL_TRACEHANDSHAKE    = "202"
	RPL_TRACEUNKNOWN      = "203"
	RPL_TRACEOPERATOR     = "204"
	RPL_TRACEUSER         = "205"
	RPL_TRACESERVER       = "206"
	RPL_TRACESERVICE      = "207"
	RPL_TRACENEWTYPE      = "208"
	RPL_TRACECLASS        = "209"
	RPL_STATSLINKINFO     = "211"
	RPL_STATSCOMMANDS     = "212"
	RPL_ENDOFSTATS        = "219"
	RPL_UMODEIS           = "221"
	RPL_SERVLIST          = "234"
	RPL_SERVLISTEND       = "235"
	RPL_STATSUPTIME       = "242"
	RPL_STATSOLINE        = "243"
	RPL_ISUPPORT          = "250"
	RPL_LUSERCLIENT       = "251"
	RPL_LUSEROP           = "252"
	RPL_LUSERUNKNOWN      = "253"
	RPL_LUSERCHANNELS     = "254"
	RPL_LUSERME           = "255"
	RPL_ADMINME           = "256"
	RPL_ADMINEMAIL        = "259"
	RPL_TRACELOG          = "261"
	RPL_TRACEEND          = "262"
	RPL_TRYAGAIN          = "263"
	RPL_AWAY              = "301"
	RPL_USERHOST          = "302"
	RPL_ISON              = "303"
	RPL_UNAWAY            = "305"
	RPL_NOWAWAY           = "306"
	RPL_WHOISUSER         = "311"
	RPL_WHOISSERVER       = "312"
	RPL_WHOISOPERATOR     = "313"
	RPL_WHOWASUSER        = "314"
	RPL_ENDOFWHO          = "315"
	RPL_WHOISIDLE         = "317"
	RPL_ENDOFWHOIS        = "318"
	RPL_WHOISCHANNELS     = "319"
	RPL_LIST              = "322"
	RPL_LISTEND           = "323"
	RPL_CHANNELMODEIS     = "324"
	RPL_UNIQOPIS          = "325"
	RPL_NOTOPIC           = "331"
	RPL_TOPIC             = "332"
	RPL_INVITING          = "341"
	RPL_SUMMONING         = "342"
	RPL_INVITELIST        = "346"
	RPL_ENDOFINVITELIST   = "347"
	RPL_EXCEPTLIST        = "348"
	RPL_ENDOFEXCEPTLIST   = "349"
	RPL_VERSION           = "351"
	RPL_WHOREPLY          = "352"
	RPL_NAMREPLY          = "353"
	RPL_LINKS             = "364"
	RPL_ENDOFLINKS        = "365"
	RPL_ENDOFNAMES        = "366"
	RPL_BANLIST           = "367"
	RPL_ENDOFBANLIST      = "368"
	RPL_ENDOFWHOWAS       = "369"
	RPL_INFO              = "371"
	RPL_MOTD              = "372"
	RPL_ENDOFINFO         = "374"
	RPL_MOTDSTART         = "375"
	RPL_ENDOFMOTD         = "376"
	RPL_YOUREOPER         = "381"
	RPL_REHASHING         = "382"
	RPL_YOURESERVICE      = "383"
	RPL_TIME              = "391"
	RPL_USERSSTART        = "392"
	RPL_USERS             = "393"
	RPL_ENDOFUSERS        = "394"
	RPL_NOUSERS           = "395"
	ERR_NOSUCHNICK        = "401"
	ERR_NOSUCHSERVER      = "402"
	ERR_NOSUCHCHANNEL     = "403"
	ERR_CANNOTSENDTOCHAN  = "404"
	ERR_TOOMANYCHANNELS   = "405"
	ERR_WASNOSUCHNICK     = "406"
	ERR_TOOMANYTARGETS    = "407"
	ERR_NOSUCHSERVICE     = "408"
	ERR_NOORIGIN          = "409"
	ERR_NORECIPIENT       = "411"
	ERR_NOTEXTTOSEND      = "412"
	ERR_NOTOPLEVEL        = "413"
	ERR_WILDTOPLEVEL      = "414"
	ERR_BADMASK           = "415"
	ERR_UNKNOWNCOMMAND    = "421"
	ERR_NOMOTD            = "422"
	ERR_NOADMININFO       = "423"
	ERR_FILEERROR         = "424"
	ERR_NONICKNAMEGIVEN   = "431"
	ERR_ERRONEUSNICKNAME  = "432"
	ERR_NICKNAMEINUSE     = "433"
	ERR_NICKCOLLISION     = "436"
	ERR_UNAVAILRESOURCE   = "437"
	ERR_USERNOTINCHANNEL  = "441"
	ERR_NOTONCHANNEL      = "442"
	ERR_USERONCHANNEL     = "443"
	ERR_NOLOGIN           = "444"
	ERR_SUMMONDISABLED    = "445"
	ERR_USERSDISABLED     = "446"
	ERR_NOTREGISTERED     = "451"
	ERR_NEEDMOREPARAMS    = "461"
	ERR_ALREADYREGISTRED  = "462"
	ERR_NOPERMFORHOST     = "463"
	ERR_PASSWDMISMATCH    = "464"
	ERR_YOUREBANNEDCREEP  = "465"
	ERR_KEYSET            = "467"
	ERR_CHANNELISFULL     = "471"
	ERR_UNKNOWNMODE       = "472"
	ERR_INVITEONLYCHAN    = "473"
	ERR_BANNEDFROMCHAN    = "474"
	ERR_BADCHANNELKEY     = "475"
	ERR_BADCHANMASK       = "476"
	ERR_NOCHANMODES       = "477"
	ERR_BANLISTFULL       = "478"
	ERR_NOPRIVILEGES      = "481"
	ERR_CHANOPRIVSNEEDED  = "482"
	ERR_CANTKILLSERVER    = "483"
	ERR_RESTRICTED        = "484"
	ERR_UNIQOPPRIVSNEEDED = "485"
	ERR_NOOPERHOST        = "491"
	ERR_UMODEUNKNOWNFLAG  = "501"
	ERR_USERSDONTMATCH    = "502"
	RPL_CUSTOM            = "999"
)<commit_msg>cleaned rfc2812 constants<commit_after>package irc

// Holds all IRC constants as defined on RFC2812
const (
	Welcome         = "001"
	Yourhost        = "002"
	Created         = "003"
	Myinfo          = "004"
	Bounce          = "005"
	Tracelink       = "200"
	Traceconnecting = "201"
	Tracehandshake  = "202"
	Traceunknown    = "203"
	Traceoperator   = "204"
	Traceuser       = "205"
	Traceserver     = "206"
	Traceservice    = "207"
	Tracenewtype    = "208"
	Traceclass      = "209"
	Statslinkinfo   = "211"
	Statscommands   = "212"
	Endofstats      = "219"
	Umodeis         = "221"
	Servlist        = "234"
	Servlistend     = "235"
	Statsuptime     = "242"
	Statsoline      = "243"
	Isupport        = "250"
	Luserclient     = "251"
	Luserop         = "252"
	Luserunknown    = "253"
	Luserchannels   = "254"
	Luserme         = "255"
	Adminme         = "256"
	Adminemail      = "259"
	Tracelog        = "261"
	Traceend        = "262"
	Tryagain        = "263"
	Away            = "301"
	Userhost        = "302"
	Ison            = "303"
	Unaway          = "305"
	Nowaway         = "306"
	Whoisuser       = "311"
	Whoisserver     = "312"
	Whoisoperator   = "313"
	Whowasuser      = "314"
	Endofwho        = "315"
	Whoisidle       = "317"
	Endofwhois      = "318"
	Whoischannels   = "319"
	List            = "322"
	Listend         = "323"
	Channelmodeis   = "324"
	Uniqopis        = "325"
	Notopic         = "331"
	Topic           = "332"
	Inviting        = "341"
	Summoning       = "342"
	Invitelist      = "346"
	Endofinvitelist = "347"
	Exceptlist      = "348"
	Endofexceptlist = "349"
	Version         = "351"
	Whoreply        = "352"
	Namreply        = "353"
	Links           = "364"
	Endoflinks      = "365"
	Endofnames      = "366"
	Banlist         = "367"
	Endofbanlist    = "368"
	Endofwhowas     = "369"
	Info            = "371"
	Motd            = "372"
	Endofinfo       = "374"
	Motdstart       = "375"
	Endofmotd       = "376"
	Youreoper       = "381"
	Rehashing       = "382"
	Youreservice    = "383"
	Time            = "391"
	Usersstart      = "392"
	Users           = "393"
	Endofusers      = "394"
	Nousers         = "395"
	Join            = "JOIN"
	Message         = "PRIVMSG"
	Part            = "PART"
	Ping            = "PING"
	Quit            = "QUIT"
	Custom          = "999"

	ErrNosuchnick        = "401"
	ErrNosuchserver      = "402"
	ErrNosuchchannel     = "403"
	ErrCannotsendtochan  = "404"
	ErrToomanychannels   = "405"
	ErrWasnosuchnick     = "406"
	ErrToomanytargets    = "407"
	ErrNosuchservice     = "408"
	ErrNoorigin          = "409"
	ErrNorecipient       = "411"
	ErrNotexttosend      = "412"
	ErrNotoplevel        = "413"
	ErrWildtoplevel      = "414"
	ErrBadmask           = "415"
	ErrUnknowncommand    = "421"
	ErrNomotd            = "422"
	ErrNoadmininfo       = "423"
	ErrFileerror         = "424"
	ErrNonicknamegiven   = "431"
	ErrErroneusnickname  = "432"
	ErrNicknameinuse     = "433"
	ErrNickcollision     = "436"
	ErrUnavailresource   = "437"
	ErrUsernotinchannel  = "441"
	ErrNotonchannel      = "442"
	ErrUseronchannel     = "443"
	ErrNologin           = "444"
	ErrSummondisabled    = "445"
	ErrUsersdisabled     = "446"
	ErrNotregistered     = "451"
	ErrNeedmoreparams    = "461"
	ErrAlreadyregistred  = "462"
	ErrNopermforhost     = "463"
	ErrPasswdmismatch    = "464"
	ErrYourebannedcreep  = "465"
	ErrKeyset            = "467"
	ErrChannelisfull     = "471"
	ErrUnknownmode       = "472"
	ErrInviteonlychan    = "473"
	ErrBannedfromchan    = "474"
	ErrBadchannelkey     = "475"
	ErrBadchanmask       = "476"
	ErrNochanmodes       = "477"
	ErrBanlistfull       = "478"
	ErrNoprivileges      = "481"
	ErrChanoprivsneeded  = "482"
	ErrCantkillserver    = "483"
	ErrRestricted        = "484"
	ErrUniqopprivsneeded = "485"
	ErrNooperhost        = "491"
	ErrUmodeunknownflag  = "501"
	ErrUsersdontmatch    = "502"
)
<|endoftext|>