<commit_before>package viewer

import "github.com/jayjanssen/myq-tools2/loader"

// Single line help for the view
func (v View) GetShortHelp() string {
	return ""
}

// Detailed multi-line help for the view
func (v View) GetHelp() []string {
	return []string{""}
}

// A list of sources that this view requires
func (v View) GetSources() []loader.Source {
	return []loader.Source{
		loader.Source{},
	}
}

// Header for this view, unclear if state is needed
func (v View) GetHeader(state *loader.State) []string {
	return []string{""}
}

// Data for this view based on the state
func (v View) GetData(state *loader.State) []string {
	return []string{""}
}

// Live views output the current timestamp whereas Runtime views output the delta in Uptime since the first state
func (v View) SetLive() {

}

func (v View) SetRuntime() {

}
<commit_msg>interface tweaks<commit_after>package viewer

import "github.com/jayjanssen/myq-tools2/loader"

// Single line help for the view
func (v View) GetShortHelp() string {
	return ""
}

// Detailed multi-line help for the view
func (v View) GetDetailedHelp() []string {
	return []string{""}
}

// A list of sources that this view requires
func (v View) GetSources() ([]*loader.Source, error) {
	return []*loader.Source{
		&loader.Source{},
	}, nil
}

// Header for this view, unclear if state is needed
func (v View) GetHeader(state *loader.State) []string {
	return []string{""}
}

// Data for this view based on the state
func (v View) GetData(state *loader.State) []string {
	return []string{""}
}

// Live views output the current timestamp whereas Runtime views output the delta in Uptime since the first state
func (v View) SetLive() {

}

func (v View) SetRuntime() {

}
<|endoftext|>


<commit_before>package complete

import (
	"fmt"
	"os"
	"runtime"
	"testing"

	"github.com/elves/elvish/cli/addons/completion"
	"github.com/elves/elvish/diag"
	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/tt"
	"github.com/elves/elvish/util"
)

var Args = tt.Args

// An implementation of PureEvaler useful in tests.
type testEvaler struct {
	externals  []string
	specials   []string
	namespaces []string
	variables  map[string][]string
}

func feed(f func(string), ss []string) {
	for _, s := range ss {
		f(s)
	}
}

func (ev testEvaler) EachExternal(f func(string)) { feed(f, ev.externals) }
func (ev testEvaler) EachSpecial(f func(string))  { feed(f, ev.specials) }
func (ev testEvaler) EachNs(f func(string))       { feed(f, ev.namespaces) }

func (ev testEvaler) EachVariableInNs(ns string, f func(string)) {
	feed(f, ev.variables[ns])
}

func (ev testEvaler) PurelyEvalPartialCompound(
	cn *parse.Compound, upto *parse.Indexing) (string, error) {
	return (*eval.Evaler)(nil).PurelyEvalPartialCompound(cn, upto)
}

func (ev testEvaler) PurelyEvalCompound(cn *parse.Compound) (string, error) {
	return (*eval.Evaler)(nil).PurelyEvalCompound(cn)
}

func (ev testEvaler) PurelyEvalPrimary(pn *parse.Primary) interface{} {
	return (*eval.Evaler)(nil).PurelyEvalPrimary(pn)
}

func setupFs() func() {
	return util.InTestDirWithSetup(util.Dir{
		"exe":     util.File{Perm: 0755, Content: ""},
		"non-exe": "",
		"d": util.Dir{
			"exe": util.File{Perm: 0755, Content: ""},
		},
	})
}

func TestComplete(t *testing.T) {
	cleanupFs := setupFs()
	defer cleanupFs()

	cfg := Config{
		Filter: PrefixFilter,
		PureEvaler: testEvaler{
			externals: []string{"ls", "make"},
			specials:  []string{"if", "for"},
			variables: map[string][]string{
				"":     []string{"foo", "bar", "fn~", "ns:"},
				"ns1:": []string{"lorem"},
				"ns2:": []string{"ipsum"},
			},
			namespaces: []string{"ns1:", "ns2:"},
		},
	}

	cfgWithCompleteArg := Config{
		PureEvaler: cfg.PureEvaler,
		CompleteArg: func(args []string) ([]RawItem, error) {
			item := noQuoteItem(fmt.Sprintf("%#v", args))
			return []RawItem{item}, nil
		},
	}

	allFileNameItems := []completion.Item{
		{ToShow: "d", ToInsert: withPathSeparator("d")},
		{ToShow: "exe", ToInsert: "exe "},
		{ToShow: "non-exe", ToInsert: "non-exe "},
	}

	allCommandItems := []completion.Item{
		c("bar = "), c("fn"), c("foo = "), c("for"), c("if"), c("ls"), c("make"),
		c("ns:"),
	}

	tt.Test(t, tt.Fn("Complete", Complete), tt.Table{
		// Complete arguments using the fallback filename completer.
		Args(cb("ls "), cfg).Rets(
			&Result{
				Name: "argument", Replace: r(3, 3),
				Items: allFileNameItems},
			nil),
		Args(cb("ls e"), cfg).Rets(
			&Result{
				Name: "argument", Replace: r(3, 4),
				Items: []completion.Item{{ToShow: "exe", ToInsert: "exe "}}},
			nil),
		// Custom arg completer, new argument
		Args(cb("ls a "), cfgWithCompleteArg).Rets(
			&Result{
				Name: "argument", Replace: r(5, 5),
				Items: []completion.Item{c(`[]string{"ls", "a", ""}`)}},
			nil),
		Args(cb("ls a b"), cfgWithCompleteArg).Rets(
			&Result{
				Name: "argument", Replace: r(5, 6),
				Items: []completion.Item{c(`[]string{"ls", "a", "b"}`)}},
			nil),

		// Complete commands at an empty buffer, generating special forms,
		// externals, functions, namespaces and variable assignments.
		Args(cb(""), cfg).Rets(
			&Result{Name: "command", Replace: r(0, 0), Items: allCommandItems},
			nil),
		// Complete at an empty closure.
		Args(cb("{ "), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete after a newline.
		Args(cb("a\n"), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete after a semicolon.
		Args(cb("a;"), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete after a pipe.
		Args(cb("a|"), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete at the beginning of output capture.
		Args(cb("a ("), cfg).Rets(
			&Result{Name: "command", Replace: r(3, 3), Items: allCommandItems},
			nil),
		// Complete at the beginning of exception capture.
		Args(cb("a ?("), cfg).Rets(
			&Result{Name: "command", Replace: r(4, 4), Items: allCommandItems},
			nil),

		// Complete external commands with the e: prefix.
		Args(cb("e:"), cfg).Rets(
			&Result{
				Name: "command", Replace: r(0, 2),
				Items: []completion.Item{c("e:ls"), c("e:make")}},
			nil),

		// Complete local external commands.
		Args(cb("./"), cfg).Rets(
			&Result{
				Name: "command", Replace: r(0, 2),
				Items: []completion.Item{
					{ToShow: "./d", ToInsert: withPathSeparator("./d")},
					{ToShow: "./exe", ToInsert: "./exe "},
				}},
			nil),

		// TODO(xiaq): Add tests for completing indicies.

		// Complete filenames for redirection.
		Args(cb("p >"), cfg).Rets(
			&Result{Name: "redir", Replace: r(3, 3), Items: allFileNameItems},
			nil),
		Args(cb("p > e"), cfg).Rets(
			&Result{
				Name: "redir", Replace: r(4, 5),
				Items: []completion.Item{
					{ToShow: "exe", ToInsert: "exe "},
				}},
			nil),

		// Completing variables.
		Args(cb("p $"), cfg).Rets(
			&Result{
				Name: "variable", Replace: r(3, 3),
				Items: []completion.Item{
					c("bar"), c("fn~"), c("foo"), c("ns1:"), c("ns2:"), c("ns:")}},
			nil),
		Args(cb("p $f"), cfg).Rets(
			&Result{
				Name: "variable", Replace: r(3, 4),
				Items: []completion.Item{c("fn~"), c("foo")}},
			nil),
		//       0123456
		Args(cb("p $ns1:"), cfg).Rets(
			&Result{
				Name: "variable", Replace: r(7, 7),
				Items: []completion.Item{c("lorem")}},
			nil),
	})

	// Symlinks are only available on UNIX.
	if goos := runtime.GOOS; goos != "windows" && goos != "plan9" {
		err := os.Symlink("d", "d2")
		if err != nil {
			panic(err)
		}
		tt.Test(t, tt.Fn("Complete", Complete), tt.Table{
			// Filename completion treats symlink to directories as directories.
			//       01234
			Args(cb("p > d"), cfg).Rets(
				&Result{
					Name: "redir", Replace: r(4, 5),
					Items: []completion.Item{
						{ToShow: "d", ToInsert: withPathSeparator("d")},
						{ToShow: "d2", ToInsert: "d2/"},
					}},
				nil,
			),
		})
	}
}

func cb(s string) CodeBuffer { return CodeBuffer{s, len(s)} }

func c(s string) completion.Item { return completion.Item{ToShow: s, ToInsert: s} }

func r(i, j int) diag.Ranging { return diag.Ranging{From: i, To: j} }

func withPathSeparator(d string) string { return d + parse.Quote(string(os.PathSeparator)) }
<commit_msg>cliedit/complete: Fix test for Windows.<commit_after>package complete

import (
	"fmt"
	"os"
	"runtime"
	"testing"

	"github.com/elves/elvish/cli/addons/completion"
	"github.com/elves/elvish/diag"
	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/tt"
	"github.com/elves/elvish/util"
)

var Args = tt.Args

// An implementation of PureEvaler useful in tests.
type testEvaler struct {
	externals  []string
	specials   []string
	namespaces []string
	variables  map[string][]string
}

func feed(f func(string), ss []string) {
	for _, s := range ss {
		f(s)
	}
}

func (ev testEvaler) EachExternal(f func(string)) { feed(f, ev.externals) }
func (ev testEvaler) EachSpecial(f func(string))  { feed(f, ev.specials) }
func (ev testEvaler) EachNs(f func(string))       { feed(f, ev.namespaces) }

func (ev testEvaler) EachVariableInNs(ns string, f func(string)) {
	feed(f, ev.variables[ns])
}

func (ev testEvaler) PurelyEvalPartialCompound(
	cn *parse.Compound, upto *parse.Indexing) (string, error) {
	return (*eval.Evaler)(nil).PurelyEvalPartialCompound(cn, upto)
}

func (ev testEvaler) PurelyEvalCompound(cn *parse.Compound) (string, error) {
	return (*eval.Evaler)(nil).PurelyEvalCompound(cn)
}

func (ev testEvaler) PurelyEvalPrimary(pn *parse.Primary) interface{} {
	return (*eval.Evaler)(nil).PurelyEvalPrimary(pn)
}

func setupFs() func() {
	return util.InTestDirWithSetup(util.Dir{
		"a.exe":   util.File{Perm: 0755, Content: ""},
		"non-exe": "",
		"d": util.Dir{
			"a.exe": util.File{Perm: 0755, Content: ""},
		},
	})
}

func TestComplete(t *testing.T) {
	cleanupFs := setupFs()
	defer cleanupFs()

	cfg := Config{
		Filter: PrefixFilter,
		PureEvaler: testEvaler{
			externals: []string{"ls", "make"},
			specials:  []string{"if", "for"},
			variables: map[string][]string{
				"":     []string{"foo", "bar", "fn~", "ns:"},
				"ns1:": []string{"lorem"},
				"ns2:": []string{"ipsum"},
			},
			namespaces: []string{"ns1:", "ns2:"},
		},
	}

	cfgWithCompleteArg := Config{
		PureEvaler: cfg.PureEvaler,
		CompleteArg: func(args []string) ([]RawItem, error) {
			item := noQuoteItem(fmt.Sprintf("%#v", args))
			return []RawItem{item}, nil
		},
	}

	allFileNameItems := []completion.Item{
		{ToShow: "d", ToInsert: withPathSeparator("d")},
		{ToShow: "a.exe", ToInsert: "a.exe "},
		{ToShow: "non-exe", ToInsert: "non-exe "},
	}

	allCommandItems := []completion.Item{
		c("bar = "), c("fn"), c("foo = "), c("for"), c("if"), c("ls"), c("make"),
		c("ns:"),
	}

	tt.Test(t, tt.Fn("Complete", Complete), tt.Table{
		// Complete arguments using the fallback filename completer.
		Args(cb("ls "), cfg).Rets(
			&Result{
				Name: "argument", Replace: r(3, 3),
				Items: allFileNameItems},
			nil),
		Args(cb("ls e"), cfg).Rets(
			&Result{
				Name: "argument", Replace: r(3, 4),
				Items: []completion.Item{{ToShow: "a.exe", ToInsert: "a.exe "}}},
			nil),
		// Custom arg completer, new argument
		Args(cb("ls a "), cfgWithCompleteArg).Rets(
			&Result{
				Name: "argument", Replace: r(5, 5),
				Items: []completion.Item{c(`[]string{"ls", "a", ""}`)}},
			nil),
		Args(cb("ls a b"), cfgWithCompleteArg).Rets(
			&Result{
				Name: "argument", Replace: r(5, 6),
				Items: []completion.Item{c(`[]string{"ls", "a", "b"}`)}},
			nil),

		// Complete commands at an empty buffer, generating special forms,
		// externals, functions, namespaces and variable assignments.
		Args(cb(""), cfg).Rets(
			&Result{Name: "command", Replace: r(0, 0), Items: allCommandItems},
			nil),
		// Complete at an empty closure.
		Args(cb("{ "), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete after a newline.
		Args(cb("a\n"), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete after a semicolon.
		Args(cb("a;"), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete after a pipe.
		Args(cb("a|"), cfg).Rets(
			&Result{Name: "command", Replace: r(2, 2), Items: allCommandItems},
			nil),
		// Complete at the beginning of output capture.
		Args(cb("a ("), cfg).Rets(
			&Result{Name: "command", Replace: r(3, 3), Items: allCommandItems},
			nil),
		// Complete at the beginning of exception capture.
		Args(cb("a ?("), cfg).Rets(
			&Result{Name: "command", Replace: r(4, 4), Items: allCommandItems},
			nil),

		// Complete external commands with the e: prefix.
		Args(cb("e:"), cfg).Rets(
			&Result{
				Name: "command", Replace: r(0, 2),
				Items: []completion.Item{c("e:ls"), c("e:make")}},
			nil),

		// TODO(xiaq): Add tests for completing indicies.

		// Complete filenames for redirection.
		Args(cb("p >"), cfg).Rets(
			&Result{Name: "redir", Replace: r(3, 3), Items: allFileNameItems},
			nil),
		Args(cb("p > e"), cfg).Rets(
			&Result{
				Name: "redir", Replace: r(4, 5),
				Items: []completion.Item{
					{ToShow: "a.exe", ToInsert: "a.exe "},
				}},
			nil),

		// Completing variables.
		Args(cb("p $"), cfg).Rets(
			&Result{
				Name: "variable", Replace: r(3, 3),
				Items: []completion.Item{
					c("bar"), c("fn~"), c("foo"), c("ns1:"), c("ns2:"), c("ns:")}},
			nil),
		Args(cb("p $f"), cfg).Rets(
			&Result{
				Name: "variable", Replace: r(3, 4),
				Items: []completion.Item{c("fn~"), c("foo")}},
			nil),
		//       0123456
		Args(cb("p $ns1:"), cfg).Rets(
			&Result{
				Name: "variable", Replace: r(7, 7),
				Items: []completion.Item{c("lorem")}},
			nil),
	})

	// Symlinks and executable bits are not available on Windows.
	if goos := runtime.GOOS; goos != "windows" {
		err := os.Symlink("d", "d2")
		if err != nil {
			panic(err)
		}
		tt.Test(t, tt.Fn("Complete", Complete), tt.Table{
			// Filename completion treats symlink to directories as directories.
			//       01234
			Args(cb("p > d"), cfg).Rets(
				&Result{
					Name: "redir", Replace: r(4, 5),
					Items: []completion.Item{
						{ToShow: "d", ToInsert: withPathSeparator("d")},
						{ToShow: "d2", ToInsert: "d2/"},
					}},
				nil,
			),

			// Complete local external commands.
			//
			// TODO(xiaq): Make this test applicable to Windows by using a
			// different criteria for executable files on Window.
			Args(cb("./"), cfg).Rets(
				&Result{
					Name: "command", Replace: r(0, 2),
					Items: []completion.Item{
						{ToShow: "./d", ToInsert: withPathSeparator("./d")},
						{ToShow: "./a.exe", ToInsert: "./a.exe "},
					}},
				nil),
		})
	}
}

func cb(s string) CodeBuffer { return CodeBuffer{s, len(s)} }

func c(s string) completion.Item { return completion.Item{ToShow: s, ToInsert: s} }

func r(i, j int) diag.Ranging { return diag.Ranging{From: i, To: j} }

func withPathSeparator(d string) string { return d + parse.Quote(string(os.PathSeparator)) }
<|endoftext|>


<commit_before>package nodes

import (
	"github.com/mitchellh/mapstructure"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
)

// Node represents a back-end device, usually a virtual machine, that can
// handle traffic. It is assigned traffic based on its parent load balancer.
type Node struct {
	// The IP address or CIDR for this back-end node.
	Address string

	// The unique ID for this node.
	ID int

	// The port on which traffic is sent and received.
	Port int

	// The node's status.
	Status Status

	// The node's condition.
	Condition Condition

	// The priority at which this node will receive traffic if a weighted
	// algorithm is used by its parent load balancer. Ranges from 1 to 100.
	Weight int

	// Type of node.
	Type Type
}

// Type indicates whether the node is of a PRIMARY or SECONDARY nature.
type Type string

const (
	// PRIMARY nodes are in the normal rotation to receive traffic from the load
	// balancer.
	PRIMARY Type = "PRIMARY"

	// SECONDARY nodes are only in the rotation to receive traffic from the load
	// balancer when all the primary nodes fail. This provides a failover feature
	// that automatically routes traffic to the secondary node in the event that
	// the primary node is disabled or in a failing state. Note that active
	// health monitoring must be enabled on the load balancer to enable the
	// failover feature to the secondary node.
	SECONDARY Type = "SECONDARY"
)

type Condition string

const (
	// ENABLED indicates that the node is permitted to accept new connections.
	ENABLED Condition = "ENABLED"

	// DISABLED indicates that the node is not permitted to accept any new
	// connections regardless of session persistence configuration. Existing
	// connections are forcibly terminated.
	DISABLED Condition = "DISABLED"

	// DRAINING indicates that the node is allowed to service existing
	// established connections and connections that are being directed to it as a
	// result of the session persistence configuration.
	DRAINING Condition = "DRAINING"
)

// Status indicates whether the node can accept service traffic. If a node is
// not listening on its port or does not meet the conditions of the defined
// active health check for the load balancer, then the load balancer does not
// forward connections and its status is listed as OFFLINE
type Status string

const (
	// ONLINE indicates that the node is healthy and capable of receiving traffic
	// from the load balancer.
	ONLINE Status = "ONLINE"

	// OFFLINE indicates that the node is not in a position to receive service
	// traffic. It is usually switched into this state when a health check is not
	// satisfied with the node's response time.
	OFFLINE Status = "OFFLINE"
)

// NodePage is the page returned by a pager when traversing over a collection
// of nodes.
type NodePage struct {
	pagination.SinglePageBase
}

// IsEmpty checks whether a NodePage struct is empty.
func (p NodePage) IsEmpty() (bool, error) {
	is, err := ExtractNodes(p)
	if err != nil {
		return true, nil
	}
	return len(is) == 0, nil
}

func commonExtractNodes(body interface{}) ([]Node, error) {
	var resp struct {
		Nodes []Node `mapstructure:"nodes" json:"nodes"`
	}

	err := mapstructure.Decode(body, &resp)

	return resp.Nodes, err
}

// ExtractNodes accepts a Page struct, specifically a NodePage struct, and
// extracts the elements into a slice of Node structs. In other words, a
// generic collection is mapped into a relevant slice.
func ExtractNodes(page pagination.Page) ([]Node, error) {
	return commonExtractNodes(page.(NodePage).Body)
}

// CreateResult represents the result of a create operation. Since multiple
// nodes can be added in one operation, this result represents multiple nodes
// and should be treated as a typical pagination Page. Use its ExtractNodes
// method to get out a slice of Node structs.
type CreateResult struct {
	pagination.SinglePageBase
}

func (res CreateResult) ExtractNodes() ([]Node, error) {
	return commonExtractNodes(res.Body)
}

type DeleteResult struct {
	gophercloud.ErrResult
}

type commonResult struct {
	gophercloud.Result
}

type GetResult struct {
	commonResult
}

type UpdateResult struct {
	gophercloud.ErrResult
}

func (r commonResult) Extract() (*Node, error) {
	if r.Err != nil {
		return nil, r.Err
	}

	var response struct {
		Node Node `mapstructure:"node"`
	}

	err := mapstructure.Decode(r.Body, &response)

	return &response.Node, err
}

type NodeEvent struct {
	ID              int
	DetailedMessage string
	NodeID          int
	Type            string
	Description     string
	Category        string
	Severity        string
	RelativeURI     string
	AccountID       int
	LoadBalancerID  int
	Title           string
	Author          string
	Created         string
}

type NodeEventPage struct {
	pagination.SinglePageBase
}

func (r NodeEventPage) IsEmpty() (bool, error) {
	is, err := ExtractNodeEvents(r)
	if err != nil {
		return true, err
	}
	return len(is) == 0, nil
}

func ExtractNodeEvents(page pagination.Page) ([]NodeEvent, error) {
	var resp struct {
		Events []NodeEvent `mapstructure:"nodeServiceEvents" json:"nodeServiceEvents"`
	}

	err := mapstructure.Decode(page.(NodeEventPage).Body, &resp)

	return resp.Events, err
}
<commit_msg>Finalizing node docs :pencil:<commit_after>package nodes

import (
	"github.com/mitchellh/mapstructure"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
)

// Node represents a back-end device, usually a virtual machine, that can
// handle traffic. It is assigned traffic based on its parent load balancer.
type Node struct {
	// The IP address or CIDR for this back-end node.
	Address string

	// The unique ID for this node.
	ID int

	// The port on which traffic is sent and received.
	Port int

	// The node's status.
	Status Status

	// The node's condition.
	Condition Condition

	// The priority at which this node will receive traffic if a weighted
	// algorithm is used by its parent load balancer. Ranges from 1 to 100.
	Weight int

	// Type of node.
	Type Type
}

// Type indicates whether the node is of a PRIMARY or SECONDARY nature.
type Type string

const (
	// PRIMARY nodes are in the normal rotation to receive traffic from the load
	// balancer.
	PRIMARY Type = "PRIMARY"

	// SECONDARY nodes are only in the rotation to receive traffic from the load
	// balancer when all the primary nodes fail. This provides a failover feature
	// that automatically routes traffic to the secondary node in the event that
	// the primary node is disabled or in a failing state. Note that active
	// health monitoring must be enabled on the load balancer to enable the
	// failover feature to the secondary node.
	SECONDARY Type = "SECONDARY"
)

// Condition represents the condition of a node.
type Condition string

const (
	// ENABLED indicates that the node is permitted to accept new connections.
	ENABLED Condition = "ENABLED"

	// DISABLED indicates that the node is not permitted to accept any new
	// connections regardless of session persistence configuration. Existing
	// connections are forcibly terminated.
	DISABLED Condition = "DISABLED"

	// DRAINING indicates that the node is allowed to service existing
	// established connections and connections that are being directed to it as a
	// result of the session persistence configuration.
	DRAINING Condition = "DRAINING"
)

// Status indicates whether the node can accept service traffic. If a node is
// not listening on its port or does not meet the conditions of the defined
// active health check for the load balancer, then the load balancer does not
// forward connections and its status is listed as OFFLINE
type Status string

const (
	// ONLINE indicates that the node is healthy and capable of receiving traffic
	// from the load balancer.
	ONLINE Status = "ONLINE"

	// OFFLINE indicates that the node is not in a position to receive service
	// traffic. It is usually switched into this state when a health check is not
	// satisfied with the node's response time.
	OFFLINE Status = "OFFLINE"
)

// NodePage is the page returned by a pager when traversing over a collection
// of nodes.
type NodePage struct {
	pagination.SinglePageBase
}

// IsEmpty checks whether a NodePage struct is empty.
func (p NodePage) IsEmpty() (bool, error) {
	is, err := ExtractNodes(p)
	if err != nil {
		return true, nil
	}
	return len(is) == 0, nil
}

func commonExtractNodes(body interface{}) ([]Node, error) {
	var resp struct {
		Nodes []Node `mapstructure:"nodes" json:"nodes"`
	}

	err := mapstructure.Decode(body, &resp)

	return resp.Nodes, err
}

// ExtractNodes accepts a Page struct, specifically a NodePage struct, and
// extracts the elements into a slice of Node structs. In other words, a
// generic collection is mapped into a relevant slice.
func ExtractNodes(page pagination.Page) ([]Node, error) {
	return commonExtractNodes(page.(NodePage).Body)
}

// CreateResult represents the result of a create operation. Since multiple
// nodes can be added in one operation, this result represents multiple nodes
// and should be treated as a typical pagination Page. Use its ExtractNodes
// method to get out a slice of Node structs.
type CreateResult struct {
	pagination.SinglePageBase
}

// ExtractNodes extracts a slice of Node structs from a CreateResult.
func (res CreateResult) ExtractNodes() ([]Node, error) {
	return commonExtractNodes(res.Body)
}

// DeleteResult represents the result of a delete operation.
type DeleteResult struct {
	gophercloud.ErrResult
}

type commonResult struct {
	gophercloud.Result
}

// GetResult represents the result of a get operation.
type GetResult struct {
	commonResult
}

// UpdateResult represents the result of an update operation.
type UpdateResult struct {
	gophercloud.ErrResult
}

func (r commonResult) Extract() (*Node, error) {
	if r.Err != nil {
		return nil, r.Err
	}

	var response struct {
		Node Node `mapstructure:"node"`
	}

	err := mapstructure.Decode(r.Body, &response)

	return &response.Node, err
}

// NodeEvent represents a service event that occurred between a node and a
// load balancer.
type NodeEvent struct {
	ID              int
	DetailedMessage string
	NodeID          int
	Type            string
	Description     string
	Category        string
	Severity        string
	RelativeURI     string
	AccountID       int
	LoadBalancerID  int
	Title           string
	Author          string
	Created         string
}

// NodeEventPage is a concrete type which embeds the common SinglePageBase
// struct, and is used when traversing node event collections.
type NodeEventPage struct {
	pagination.SinglePageBase
}

// IsEmpty is a concrete function which indicates whether an NodeEventPage is
// empty or not.
func (r NodeEventPage) IsEmpty() (bool, error) {
	is, err := ExtractNodeEvents(r)
	if err != nil {
		return true, err
	}
	return len(is) == 0, nil
}

// ExtractNodeEvents accepts a Page struct, specifically a NodeEventPage
// struct, and extracts the elements into a slice of NodeEvent structs. In
// other words, the collection is mapped into a relevant slice.
func ExtractNodeEvents(page pagination.Page) ([]NodeEvent, error) {
	var resp struct {
		Events []NodeEvent `mapstructure:"nodeServiceEvents" json:"nodeServiceEvents"`
	}

	err := mapstructure.Decode(page.(NodeEventPage).Body, &resp)

	return resp.Events, err
}
<|endoftext|>


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

package fit_test

import (
	"bufio"
	"image/color"
	"math"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"testing"

	"go-hep.org/x/hep/fit"
	"go-hep.org/x/hep/hplot"
	"gonum.org/v1/gonum/floats"
	"gonum.org/v1/plot/cmpimg"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/vg"
)

func TestCurve1D(t *testing.T) {
	checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_gaussian, t, "gauss-plot.png")
	checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_exponential, t, "exp-plot.png")
	checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_poly, t, "poly-plot.png")
	checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_powerlaw, t, "powerlaw-plot.png")
}

func TestCurve1DGaussianDefaultOpt(t *testing.T) {
	checkPlot(cmpimg.CheckPlot)(func() {
		var (
			cst   = 3.0
			mean  = 30.0
			sigma = 20.0
			want  = []float64{cst, mean, sigma}
		)

		xdata, ydata, err := readXY("testdata/gauss-data.txt")
		if err != nil {
			t.Fatal(err)
		}

		gauss := func(x, cst, mu, sigma float64) float64 {
			v := (x - mu)
			return cst * math.Exp(-v*v/sigma)
		}

		res, err := fit.Curve1D(
			fit.Func1D{
				F: func(x float64, ps []float64) float64 {
					return gauss(x, ps[0], ps[1], ps[2])
				},
				X:  xdata,
				Y:  ydata,
				Ps: []float64{10, 10, 10},
			},
			nil, nil,
		)
		if err != nil {
			t.Fatal(err)
		}

		if err := res.Status.Err(); err != nil {
			t.Fatal(err)
		}
		if got := res.X; !floats.EqualApprox(got, want, 1e-3) {
			t.Fatalf("got= %v\nwant=%v\n", got, want)
		}

		{
			p := hplot.New()
			p.X.Label.Text = "Gauss"
			p.Y.Label.Text = "y-data"

			s := hplot.NewS2D(hplot.ZipXY(xdata, ydata))
			s.Color = color.RGBA{0, 0, 255, 255}
			p.Add(s)

			f := plotter.NewFunction(func(x float64) float64 {
				return gauss(x, res.X[0], res.X[1], res.X[2])
			})
			f.Color = color.RGBA{255, 0, 0, 255}
			f.Samples = 1000
			p.Add(f)

			p.Add(plotter.NewGrid())

			err := p.Save(20*vg.Centimeter, -1, "testdata/gauss-plot-default-opt.png")
			if err != nil {
				t.Fatal(err)
			}
		}
	}, t, "gauss-plot-default-opt.png")
}

func genXY(n int, f func(x float64, ps []float64) float64, ps []float64, xmin, xmax float64) ([]float64, []float64) {
	xdata := make([]float64, n)
	ydata := make([]float64, n)
	rnd := rand.New(rand.NewSource(1234))
	xstep := (xmax - xmin) / float64(n)
	p := make([]float64, len(ps))
	for i := 0; i < n; i++ {
		x := xmin + xstep*float64(i)
		for j := range p {
			v := rnd.NormFloat64()
			p[j] = ps[j] + v*0.2
		}
		xdata[i] = x
		ydata[i] = f(x, p)
	}
	return xdata, ydata
}

func readXY(fname string) (xs, ys []float64, err error) {
	f, err := os.Open(fname)
	if err != nil {
		return xs, ys, err
	}
	defer f.Close()

	scan := bufio.NewScanner(f)
	for scan.Scan() {
		line := scan.Text()
		toks := strings.Split(line, " ")
		x, err := strconv.ParseFloat(toks[0], 64)
		if err != nil {
			return xs, ys, err
		}
		xs = append(xs, x)

		y, err := strconv.ParseFloat(toks[1], 64)
		if err != nil {
			return xs, ys, err
		}
		ys = append(ys, y)
	}

	return
}

func readXYerr(fname string) (xs, ys, yerrs []float64, err error) {
	f, err := os.Open(fname)
	if err != nil {
		return xs, ys, yerrs, err
	}
	defer f.Close()

	scan := bufio.NewScanner(f)
	for scan.Scan() {
		line := scan.Text()
		toks := strings.Split(line, " ")
		x, err := strconv.ParseFloat(toks[0], 64)
		if err != nil {
			return xs, ys, yerrs, err
		}
		xs = append(xs, x)

		y, err := strconv.ParseFloat(toks[1], 64)
		if err != nil {
			return xs, ys, yerrs, err
		}
		ys = append(ys, y)

		yerr, err := strconv.ParseFloat(toks[2], 64)
		if err != nil {
			return xs, ys, yerrs, err
		}
		yerrs = append(yerrs, yerr)
	}

	return
}
<commit_msg>fit: remove exponential example from darwin tests<commit_after>// Copyright ©2017 The go-hep Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fit_test

import (
	"bufio"
	"image/color"
	"math"
	"math/rand"
	"os"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"go-hep.org/x/hep/fit"
	"go-hep.org/x/hep/hplot"
	"gonum.org/v1/gonum/floats"
	"gonum.org/v1/plot/cmpimg"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/vg"
)

func TestCurve1D(t *testing.T) {
	checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_gaussian, t, "gauss-plot.png")
	if runtime.GOOS != "darwin" {
		checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_exponential, t, "exp-plot.png")
	}
	checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_poly, t, "poly-plot.png")
	checkPlot(cmpimg.CheckPlot)(ExampleCurve1D_powerlaw, t, "powerlaw-plot.png")
}

func TestCurve1DGaussianDefaultOpt(t *testing.T) {
	checkPlot(cmpimg.CheckPlot)(func() {
		var (
			cst   = 3.0
			mean  = 30.0
			sigma = 20.0
			want  = []float64{cst, mean, sigma}
		)

		xdata, ydata, err := readXY("testdata/gauss-data.txt")
		if err != nil {
			t.Fatal(err)
		}

		gauss := func(x, cst, mu, sigma float64) float64 {
			v := (x - mu)
			return cst * math.Exp(-v*v/sigma)
		}

		res, err := fit.Curve1D(
			fit.Func1D{
				F: func(x float64, ps []float64) float64 {
					return gauss(x, ps[0], ps[1], ps[2])
				},
				X:  xdata,
				Y:  ydata,
				Ps: []float64{10, 10, 10},
			},
			nil, nil,
		)
		if err != nil {
			t.Fatal(err)
		}

		if err := res.Status.Err(); err != nil {
			t.Fatal(err)
		}
		if got := res.X; !floats.EqualApprox(got, want, 1e-3) {
			t.Fatalf("got= %v\nwant=%v\n", got, want)
		}

		{
			p := hplot.New()
			p.X.Label.Text = "Gauss"
			p.Y.Label.Text = "y-data"

			s := hplot.NewS2D(hplot.ZipXY(xdata, ydata))
			s.Color = color.RGBA{0, 0, 255, 255}
			p.Add(s)

			f := plotter.NewFunction(func(x float64) float64 {
				return gauss(x, res.X[0], res.X[1], res.X[2])
			})
			f.Color = color.RGBA{255, 0, 0, 255}
			f.Samples = 1000
			p.Add(f)

			p.Add(plotter.NewGrid())

			err := p.Save(20*vg.Centimeter, -1, "testdata/gauss-plot-default-opt.png")
			if err != nil {
				t.Fatal(err)
			}
		}
	}, t, "gauss-plot-default-opt.png")
}

func genXY(n int, f func(x float64, ps []float64) float64, ps []float64, xmin, xmax float64) ([]float64, []float64) {
	xdata := make([]float64, n)
	ydata := make([]float64, n)
	rnd := rand.New(rand.NewSource(1234))
	xstep := (xmax - xmin) / float64(n)
	p := make([]float64, len(ps))
	for i := 0; i < n; i++ {
		x := xmin + xstep*float64(i)
		for j := range p {
			v := rnd.NormFloat64()
			p[j] = ps[j] + v*0.2
		}
		xdata[i] = x
		ydata[i] = f(x, p)
	}
	return xdata, ydata
}

func readXY(fname string) (xs, ys []float64, err error) {
	f, err := os.Open(fname)
	if err != nil {
		return xs, ys, err
	}
	defer f.Close()

	scan := bufio.NewScanner(f)
	for scan.Scan() {
		line := scan.Text()
		toks := strings.Split(line, " ")
		x, err := strconv.ParseFloat(toks[0], 64)
		if err != nil {
			return xs, ys, err
		}
		xs = append(xs, x)

		y, err := strconv.ParseFloat(toks[1], 64)
		if err != nil {
			return xs, ys, err
		}
		ys = append(ys, y)
	}

	return
}

func readXYerr(fname string) (xs, ys, yerrs []float64, err error) {
	f, err := os.Open(fname)
	if err != nil {
		return xs, ys, yerrs, err
	}
	defer f.Close()

	scan := bufio.NewScanner(f)
	for scan.Scan() {
		line := scan.Text()
		toks := strings.Split(line, " ")
		x, err := strconv.ParseFloat(toks[0], 64)
		if err != nil {
			return xs, ys, yerrs, err
		}
		xs = append(xs, x)

		y, err := strconv.ParseFloat(toks[1], 64)
		if err != nil {
			return xs, ys, yerrs, err
		}
		ys = append(ys, y)

		yerr, err := strconv.ParseFloat(toks[2], 64)
		if err != nil {
			return xs, ys, yerrs, err
		}
		yerrs = append(yerrs, yerr)
	}

	return
}
<|endoftext|>


<commit_before>package collector

import (
	"os"
	"testing"
)

func TestArcstatsParsing(t *testing.T) {

	arcstatsFile, err := os.Open("fixtures/proc/spl/kstat/zfs/arcstats")
	if err != nil {
		t.Fatal(err)
	}
	defer arcstatsFile.Close()

	p := NewZFSMetricProvider()
	err = p.parseArcstatsProcfsFile(arcstatsFile)

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Parsed values mapped to sysctls:\n%v", p.values)

	value, err := p.Value(zfsSysctl("kstat.zfs.misc.arcstats.hits"))

	if err != nil {
		t.Fatal(err)
	}

	if value != zfsMetricValue(8772612) {
		t.Fatalf("Incorrect value parsed from procfs data")
	}

}
<commit_msg>Remove log statement.<commit_after>package collector

import (
	"os"
	"testing"
)

func TestArcstatsParsing(t *testing.T) {

	arcstatsFile, err := os.Open("fixtures/proc/spl/kstat/zfs/arcstats")
	if err != nil {
		t.Fatal(err)
	}
	defer arcstatsFile.Close()

	p := NewZFSMetricProvider()
	err = p.parseArcstatsProcfsFile(arcstatsFile)

	if err != nil {
		t.Fatal(err)
	}

	value, err := p.Value(zfsSysctl("kstat.zfs.misc.arcstats.hits"))

	if err != nil {
		t.Fatal(err)
	}

	if value != zfsMetricValue(8772612) {
		t.Fatalf("Incorrect value parsed from procfs data")
	}

}
<|endoftext|>


<commit_before>package main

// channel holds logic for finding and communicating with members of a
// channel.
//
// Usage:
//  // Construct a new channel.
//  c := newChannel(ctx, mounttable, proxy, "path/to/channel/name")
//
//  // Join the channel.
//  err := c.join()
//
//  // Get all members in the channel.
//  members, err := c.getMembers()
//
//  // Send a message to a member.
//  c.sendMessageTo(member, "message")
//
//  // Send a message to all members in the channel.
//  c.broadcastMessage("message")
//
//  // Leave the channel.
//  c.leave()

import (
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"sort"
	"time"

	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/naming"
	"v.io/v23/options"
	"v.io/v23/rpc"
	"v.io/v23/security"
	mt "v.io/v23/services/mounttable"
	"v.io/v23/services/security/access"

	_ "v.io/x/ref/profiles/roaming"

	"chat/vdl"
)

// message is a message that will be displayed in the UI.
type message struct {
	SenderName string
	Text       string
	Timestamp  time.Time
}

// chatServerMethods implements the chat server VDL interface.
type chatServerMethods struct {
	// Incoming messages get sent to messages channel.
	messages chan<- message
}

var _ vdl.ChatServerMethods = (*chatServerMethods)(nil)

func newChatServerMethods(messages chan<- message) *chatServerMethods {
	return &chatServerMethods{
		messages: messages,
	}
}

// SendMessage is called by clients to send a message to the server.
func (cs *chatServerMethods) SendMessage(call rpc.ServerCall, IncomingMessage string) error {
	remoteb, _ := security.BlessingNames(call.Context(), security.CallSideRemote)
	cs.messages <- message{
		SenderName: firstShortName(remoteb),
		Text:       IncomingMessage,
		Timestamp:  time.Now(),
	}
	return nil
}

// member is a member of the channel.
type member struct {
	// Blessings is the remote blessings of the member.  There could
	// potentially be multiple.
	Blessings []string
	// Name is the name we will display for this member.
	Name string
	// Path is the path in the mounttable where the member is mounted.
	Path string
}

// members are sortable by Name.
type byName []*member

func (b byName) Len() int           { return len(b) }
func (b byName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byName) Less(i, j int) bool { return b[i].Name < b[j].Name }

// channel interface.
type channel struct {
	// Vanadium context.
	ctx *context.T
	// The location where we mount ourselves and look for other users.
	path string
	// The implementation of the chat server.
	chatServerMethods *chatServerMethods
	// The chat server.
	server rpc.Server
	// Channel that emits incoming messages.
	messages chan message
	// Cached list of channel members.
	members []*member
}

func newChannel(ctx *context.T, mounttable, proxy, path string) (*channel, error) {
	// Set the namespace root to the mounttable passed on the command line.
	newCtx, _, err := v23.SetNewNamespace(ctx, mounttable)
	if err != nil {
		return nil, err
	}

	// Set the proxy that will be used to listen.
	listenSpec := v23.GetListenSpec(ctx)
	listenSpec.Proxy = proxy

	messages := make(chan message)

	return &channel{
		chatServerMethods: newChatServerMethods(messages),
		messages:          messages,
		path:              path,
		ctx:               newCtx,
		server:            nil,
	}, nil
}

// openAuthorizer allows RPCs from all clients.
type openAuthorizer struct{}

func (o openAuthorizer) Authorize(*context.T) error {
	return nil
}

// UserName returns a short, human-friendly representation of the chat client.
func (cr *channel) UserName() string {
	// TODO(ashankar): It is wrong to assume that
	// v23.GetPrincipal(ctx).BlessingStore().Default() returns a valid
	// "sender". Think about the "who-am-I" API and use that here instead.
	userName := fmt.Sprint(v23.GetPrincipal(cr.ctx).BlessingStore().Default())
	if sn := shortName(userName); sn != "" {
		userName = sn
	}
	return userName
}

// getLockedName picks a random name inside the channel's mounttable path and
// tries to "lock" it by settings restrictive permissions on the name.  It
// tries repeatedly until it finds an unused name that can be locked, and
// returns the locked name.
func (cr *channel) getLockedName() (string, error) {
	myPatterns := security.DefaultBlessingPatterns(v23.GetPrincipal(cr.ctx))

	// myACL is an ACL that only allows my blessing.
	myACL := access.AccessList{
		In: myPatterns,
	}
	// openACL is an ACL that allows anybody.
	openACL := access.AccessList{
		In: []security.BlessingPattern{security.AllPrincipals},
	}

	permissions := access.Permissions{
		// Give everybody the ability to read and resolve the name.
		string(mt.Resolve): openACL,
		string(mt.Read):    openACL,
		// All other permissions are only for us.
		string(mt.Admin):  myACL,
		string(mt.Create): myACL,
		string(mt.Mount):  myACL,
	}

	// Repeatedly try to SetPermissions under random names until we find a free
	// one.

	// Collisions should be rare.  25 times should be enough to find a free
	// one
	maxTries := 25
	for i := 0; i < maxTries; i++ {
		// Pick a random suffix, the hash of our default blessing and the time.
		now := time.Now().UnixNano()
		hash := sha256.Sum256([]byte(fmt.Sprintf("%s-%d", cr.UserName(), now)))
		suffix := base64.URLEncoding.EncodeToString(hash[:])

		name := naming.Join(cr.path, suffix)

		ns := v23.GetNamespace(cr.ctx)

		if err := ns.SetPermissions(cr.ctx, name, permissions, ""); err != nil {
			// Try again with a different name.
			continue
		}

		// SetPermissions succeeded!  We now own the name.
		return name, nil
	}
	return "", fmt.Errorf("Error getting a locked name.  Tried %v times but did not succeed.", maxTries)
}

// join starts a chat server and mounts it in the channel path.
func (cr *channel) join() error {
	// Create a new server.
	s, err := v23.NewServer(cr.ctx)
	if err != nil {
		return err
	}

	// Start listening for incoming connections.
	if _, err := s.Listen(v23.GetListenSpec(cr.ctx)); err != nil {
		return err
	}

	// Get a locked name in the mounttable that we can mount our server on.
	name, err := cr.getLockedName()
	if err != nil {
		return err
	}

	// Serve the chat server on the locked name.
	serverChat := vdl.ChatServer(cr.chatServerMethods)
	if err := s.Serve(name, serverChat, openAuthorizer{}); err != nil {
		return err
	}

	cr.server = s

	return nil
}

// leave stops the chat server and removes our mounted name from the
// mounttable.
func (cr *channel) leave() error {
	// Stop serving.
	cr.server.Stop()

	// Get the names we are mounted at.  Should only be one.
	names := cr.server.Status().Mounts.Names()
	// Delete the name and all sub-names in the hierarchy.
	ns := v23.GetNamespace(cr.ctx)
	for _, name := range names {
		if err := ns.Delete(cr.ctx, name, true); err != nil {
			return err
		}
	}

	cr.server = nil

	return nil
}

// newMember creates a new member object.
func (cr *channel) newMember(blessings []string, path string) *member {
	name := "unknown"
	if len(blessings) > 0 {
		// Arbitrarily choose the first blessing as the display name.
		name = shortName(blessings[0])
	}
	return &member{
		Name:      name,
		Blessings: blessings,
		Path:      path,
	}
}

// getMembers gets a list of members in the channel.
func (cr *channel) getMembers() ([]*member, error) {
	ctx, cancel := context.WithTimeout(cr.ctx, 5*time.Second)
	defer cancel()

	// Glob on the channel path for mounted members.
	globPath := cr.path + "/*"
	globChan, err := v23.GetNamespace(ctx).Glob(ctx, globPath)
	if err != nil {
		return nil, err
	}

	members := []*member{}

	for reply := range globChan {
		switch v := reply.(type) {
		case *naming.MountEntry:
			if len(v.Servers) == 0 {
				// No servers mounted at that name, only a
				// lonely ACL.  Safe to ignore.
				// TODO(nlacasse): Should there be a time-limit
				// on ACLs in the namespace?  Seems like we'll
				// have an ACL graveyard before too long.
				continue
			}

			member := cr.newMember(v.Servers[0].BlessingPatterns, v.Name)
			members = append(members, member)
		}
	}

	sort.Sort(byName(members))

	cr.members = members
	return members, nil
}

// broadcastMessage sends a message to all members in the channel.
func (cr *channel) broadcastMessage(messageText string) error {
	for _, member := range cr.members {
		// TODO(nlacasse): Sending messages async means they might get sent out of
		// order. Consider either sending them sync or maintain a queue.
		go cr.sendMessageTo(member, messageText)
	}
	return nil
}

// sendMessageTo sends a message to a particular member.  It ensures that the
// receiving server has the same blessings that the member does.
func (cr *channel) sendMessageTo(member *member, messageText string) {
	ctx, cancel := context.WithTimeout(cr.ctx, 5*time.Second)
	defer cancel()

	s := vdl.ChatClient(member.Path)

	// The AllowedServersPolicy options require that the server matches the
	// blessings we got when we globbed it.
	opts := make([]rpc.CallOpt, len(member.Blessings))
	for i, blessing := range member.Blessings {
		opts[i] = options.AllowedServersPolicy{security.BlessingPattern(blessing)}
	}

	if err := s.SendMessage(ctx, messageText, opts...); err != nil {
		return // member has disconnected.
	}
}
<commit_msg>"projects/chat": Use security.[Local/Remote]BlessingNames<commit_after>package main

// channel holds logic for finding and communicating with members of a
// channel.
//
// Usage:
//  // Construct a new channel.
//  c := newChannel(ctx, mounttable, proxy, "path/to/channel/name")
//
//  // Join the channel.
//  err := c.join()
//
//  // Get all members in the channel.
//  members, err := c.getMembers()
//
//  // Send a message to a member.
//  c.sendMessageTo(member, "message")
//
//  // Send a message to all members in the channel.
//  c.broadcastMessage("message")
//
//  // Leave the channel.
//  c.leave()

import (
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"sort"
	"time"

	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/naming"
	"v.io/v23/options"
	"v.io/v23/rpc"
	"v.io/v23/security"
	mt "v.io/v23/services/mounttable"
	"v.io/v23/services/security/access"

	_ "v.io/x/ref/profiles/roaming"

	"chat/vdl"
)

// message is a message that will be displayed in the UI.
type message struct {
	SenderName string
	Text       string
	Timestamp  time.Time
}

// chatServerMethods implements the chat server VDL interface.
type chatServerMethods struct {
	// Incoming messages get sent to messages channel.
	messages chan<- message
}

var _ vdl.ChatServerMethods = (*chatServerMethods)(nil)

func newChatServerMethods(messages chan<- message) *chatServerMethods {
	return &chatServerMethods{
		messages: messages,
	}
}

// SendMessage is called by clients to send a message to the server.
func (cs *chatServerMethods) SendMessage(call rpc.ServerCall, IncomingMessage string) error {
	remoteb, _ := security.RemoteBlessingNames(call.Context())
	cs.messages <- message{
		SenderName: firstShortName(remoteb),
		Text:       IncomingMessage,
		Timestamp:  time.Now(),
	}
	return nil
}

// member is a member of the channel.
type member struct {
	// Blessings is the remote blessings of the member.  There could
	// potentially be multiple.
	Blessings []string
	// Name is the name we will display for this member.
	Name string
	// Path is the path in the mounttable where the member is mounted.
	Path string
}

// members are sortable by Name.
type byName []*member

func (b byName) Len() int           { return len(b) }
func (b byName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byName) Less(i, j int) bool { return b[i].Name < b[j].Name }

// channel interface.
type channel struct {
	// Vanadium context.
	ctx *context.T
	// The location where we mount ourselves and look for other users.
	path string
	// The implementation of the chat server.
	chatServerMethods *chatServerMethods
	// The chat server.
	server rpc.Server
	// Channel that emits incoming messages.
	messages chan message
	// Cached list of channel members.
	members []*member
}

func newChannel(ctx *context.T, mounttable, proxy, path string) (*channel, error) {
	// Set the namespace root to the mounttable passed on the command line.
	newCtx, _, err := v23.SetNewNamespace(ctx, mounttable)
	if err != nil {
		return nil, err
	}

	// Set the proxy that will be used to listen.
	listenSpec := v23.GetListenSpec(ctx)
	listenSpec.Proxy = proxy

	messages := make(chan message)

	return &channel{
		chatServerMethods: newChatServerMethods(messages),
		messages:          messages,
		path:              path,
		ctx:               newCtx,
		server:            nil,
	}, nil
}

// openAuthorizer allows RPCs from all clients.
type openAuthorizer struct{}

func (o openAuthorizer) Authorize(*context.T) error {
	return nil
}

// UserName returns a short, human-friendly representation of the chat client.
func (cr *channel) UserName() string {
	// TODO(ashankar): It is wrong to assume that
	// v23.GetPrincipal(ctx).BlessingStore().Default() returns a valid
	// "sender". Think about the "who-am-I" API and use that here instead.
	userName := fmt.Sprint(v23.GetPrincipal(cr.ctx).BlessingStore().Default())
	if sn := shortName(userName); sn != "" {
		userName = sn
	}
	return userName
}

// getLockedName picks a random name inside the channel's mounttable path and
// tries to "lock" it by settings restrictive permissions on the name.  It
// tries repeatedly until it finds an unused name that can be locked, and
// returns the locked name.
func (cr *channel) getLockedName() (string, error) {
	myPatterns := security.DefaultBlessingPatterns(v23.GetPrincipal(cr.ctx))

	// myACL is an ACL that only allows my blessing.
	myACL := access.AccessList{
		In: myPatterns,
	}
	// openACL is an ACL that allows anybody.
	openACL := access.AccessList{
		In: []security.BlessingPattern{security.AllPrincipals},
	}

	permissions := access.Permissions{
		// Give everybody the ability to read and resolve the name.
		string(mt.Resolve): openACL,
		string(mt.Read):    openACL,
		// All other permissions are only for us.
		string(mt.Admin):  myACL,
		string(mt.Create): myACL,
		string(mt.Mount):  myACL,
	}

	// Repeatedly try to SetPermissions under random names until we find a free
	// one.

	// Collisions should be rare.  25 times should be enough to find a free
	// one
	maxTries := 25
	for i := 0; i < maxTries; i++ {
		// Pick a random suffix, the hash of our default blessing and the time.
		now := time.Now().UnixNano()
		hash := sha256.Sum256([]byte(fmt.Sprintf("%s-%d", cr.UserName(), now)))
		suffix := base64.URLEncoding.EncodeToString(hash[:])

		name := naming.Join(cr.path, suffix)

		ns := v23.GetNamespace(cr.ctx)

		if err := ns.SetPermissions(cr.ctx, name, permissions, ""); err != nil {
			// Try again with a different name.
			continue
		}

		// SetPermissions succeeded!  We now own the name.
		return name, nil
	}
	return "", fmt.Errorf("Error getting a locked name.  Tried %v times but did not succeed.", maxTries)
}

// join starts a chat server and mounts it in the channel path.
func (cr *channel) join() error {
	// Create a new server.
	s, err := v23.NewServer(cr.ctx)
	if err != nil {
		return err
	}

	// Start listening for incoming connections.
	if _, err := s.Listen(v23.GetListenSpec(cr.ctx)); err != nil {
		return err
	}

	// Get a locked name in the mounttable that we can mount our server on.
	name, err := cr.getLockedName()
	if err != nil {
		return err
	}

	// Serve the chat server on the locked name.
	serverChat := vdl.ChatServer(cr.chatServerMethods)
	if err := s.Serve(name, serverChat, openAuthorizer{}); err != nil {
		return err
	}

	cr.server = s

	return nil
}

// leave stops the chat server and removes our mounted name from the
// mounttable.
func (cr *channel) leave() error {
	// Stop serving.
	cr.server.Stop()

	// Get the names we are mounted at.  Should only be one.
	names := cr.server.Status().Mounts.Names()
	// Delete the name and all sub-names in the hierarchy.
	ns := v23.GetNamespace(cr.ctx)
	for _, name := range names {
		if err := ns.Delete(cr.ctx, name, true); err != nil {
			return err
		}
	}

	cr.server = nil

	return nil
}

// newMember creates a new member object.
func (cr *channel) newMember(blessings []string, path string) *member {
	name := "unknown"
	if len(blessings) > 0 {
		// Arbitrarily choose the first blessing as the display name.
		name = shortName(blessings[0])
	}
	return &member{
		Name:      name,
		Blessings: blessings,
		Path:      path,
	}
}

// getMembers gets a list of members in the channel.
func (cr *channel) getMembers() ([]*member, error) {
	ctx, cancel := context.WithTimeout(cr.ctx, 5*time.Second)
	defer cancel()

	// Glob on the channel path for mounted members.
	globPath := cr.path + "/*"
	globChan, err := v23.GetNamespace(ctx).Glob(ctx, globPath)
	if err != nil {
		return nil, err
	}

	members := []*member{}

	for reply := range globChan {
		switch v := reply.(type) {
		case *naming.MountEntry:
			if len(v.Servers) == 0 {
				// No servers mounted at that name, only a
				// lonely ACL.  Safe to ignore.
				// TODO(nlacasse): Should there be a time-limit
				// on ACLs in the namespace?  Seems like we'll
				// have an ACL graveyard before too long.
				continue
			}

			member := cr.newMember(v.Servers[0].BlessingPatterns, v.Name)
			members = append(members, member)
		}
	}

	sort.Sort(byName(members))

	cr.members = members
	return members, nil
}

// broadcastMessage sends a message to all members in the channel.
func (cr *channel) broadcastMessage(messageText string) error {
	for _, member := range cr.members {
		// TODO(nlacasse): Sending messages async means they might get sent out of
		// order. Consider either sending them sync or maintain a queue.
		go cr.sendMessageTo(member, messageText)
	}
	return nil
}

// sendMessageTo sends a message to a particular member.  It ensures that the
// receiving server has the same blessings that the member does.
func (cr *channel) sendMessageTo(member *member, messageText string) {
	ctx, cancel := context.WithTimeout(cr.ctx, 5*time.Second)
	defer cancel()

	s := vdl.ChatClient(member.Path)

	// The AllowedServersPolicy options require that the server matches the
	// blessings we got when we globbed it.
	opts := make([]rpc.CallOpt, len(member.Blessings))
	for i, blessing := range member.Blessings {
		opts[i] = options.AllowedServersPolicy{security.BlessingPattern(blessing)}
	}

	if err := s.SendMessage(ctx, messageText, opts...); err != nil {
		return // member has disconnected.
	}
}
<|endoftext|>


<commit_before>// 24 march 2015
package main

// TODO
// - should stub generator should provide a way to rename 'this'?
// - handle T* as a return type

import (
	"fmt"
	"os"
	"bufio"
	"strings"
	"text/template"
)

type Method struct {
	Ret			string
	Name		string
	ArgTypes		[]string
	ArgNames	[]string
	ArgsFull		[]string
}

type Interface struct {
	IsRaw	bool
	Raw		string
	Name	string
	Methods	[]*Method
}

type File []*Interface

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(1)
}

func (m *Method) loadargs(raw []string) {
	m.ArgTypes = make([]string, 0, len(raw) / 2)
	m.ArgNames = make([]string, 0, len(raw) / 2)
	m.ArgsFull = make([]string, 0, len(raw) / 2)
	for i := 0; i < len(raw); i += 2 {
		ty := raw[i]
		name := raw[i + 1]
		if ty[len(ty) - 1] == '*' {
			ty = ty[:len(ty) - 1] + " *"
			m.ArgsFull = append(m.ArgsFull, ty + name)
		} else {
			m.ArgsFull = append(m.ArgsFull, ty + " " + name)
		}
		m.ArgTypes = append(m.ArgTypes, ty)
		m.ArgNames = append(m.ArgNames, name)
	}
}

func readfile(filename string) (f File) {
	var curiface *Interface

	infile, err := os.Open(filename)
	if err != nil {
		die("error opening %s: %v", filename, err)
	}
	defer infile.Close()

	scanner := bufio.NewScanner(infile)
	for scanner.Scan() {
		s := scanner.Text()
		if s == "" {
			continue
		}
		if s[0] == 'r' {		// raw line
			f = append(f, &Interface{
				IsRaw:	true,
				Raw:		s[1:],
			})
			continue
		}
		p := strings.Split(strings.TrimSpace(s), " ")
		if p[0][0] == '#' {	// comment
			continue
		}
		if p[0] == "i" {		// new interface
			if curiface != nil {
				f = append(f, curiface)
			}
			curiface = &Interface{
				Name:	p[1],
			}
			continue
		}
		if p[0] == "m" {		// method
			m := &Method{
				Ret:		p[1],
				Name:	p[2],
			}
			m.loadargs(p[3:])
			curiface.Methods = append(curiface.Methods, m)
			continue
		}
		die("unknown line %q\n", s)
	}
	if err := scanner.Err(); err != nil {
		die("error reading %s: %v", filename, err)
	}

	if curiface != nil {
		f = append(f, curiface)
	}
	return f
}

const banner = "// generated by tools/iface2cdecl.go; do not edit"

func (m *Method) decl(curiface string, name string, this bool, args []string) string {
	s := m.Ret
	s += " "
	s += name
	s += "(" + curiface + " *"
	if this {
		s += "this"
	}
	for _, a := range args {
		s += ", " + a
	}
	s += ")"
	return s
}

func (m *Method) VtableDecl(curiface string) string {
	return m.decl(curiface, "(*" + m.Name + ")", false, m.ArgTypes)
}

func (m *Method) GlobalDecl(curiface string) string {
	return m.decl(curiface, curiface + m.Name, false, m.ArgTypes)
}

func (m *Method) GlobalDefn(curiface string) string {
	return m.decl(curiface, curiface + m.Name, true, m.ArgsFull)
}

// TODO split the for loop into its own function?
func (m *Method) ArgCallList() string {
	s := "this"
	for _, a := range m.ArgNames {
		s += ", " + a
	}
	return s
}

func ifacesOnly(f File) []*Interface {
	ifaces := make([]*Interface, 0, len(f))
	for _, i := range f {
		if !i.IsRaw {
			ifaces = append(ifaces, i)
		}
	}
	return ifaces
}

const hfileTemplate = banner + `
{{range .}}
#define {{.Name}}Type ({{.Name}}_get_type())
#define {{.Name}}(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), {{.Name}}Type, {{.Name}}))
#define Is{{.Name}}(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), {{.Name}}Type))
#define Get{{.Name}}Interface(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), {{.Name}}Type, {{.Name}}Interface))
typedef struct {{.Name}} {{.Name}};
typedef struct {{.Name}}Interface {{.Name}}Interface;
struct {{.Name}}Interface {
	GTypeInterface parent_iface;
{{$name := .Name}}{{range .Methods}}	{{.VtableDecl $name}};
{{end}}};
extern GType {{.Name}}_get_type(void);
extern void verify{{.Name}}Impl(char *, {{.Name}}Interface *);
{{$name := .Name}}{{range .Methods}}extern {{.GlobalDecl $name}};
{{end}}{{end}}`

func genhfile(f File, filename string) {
	of, err := os.Create(filename)
	if err != nil {
		die("error creating %s: %v", filename, err)
	}
	defer of.Close()

	t := template.Must(template.New("hfile").Parse(hfileTemplate))
	err = t.Execute(of, ifacesOnly(f))
	if err != nil {
		die("error generating .h file: %v", err)
	}
}

const cfileTemplate = banner + `
{{range .}}{{if .IsRaw}}{{.Raw}}
{{else}}
// {{.Name}}

G_DEFINE_INTERFACE({{.Name}}, {{.Name}}, G_TYPE_OBJECT)

{{$name := .Name}}{{range .Methods}}{{.GlobalDefn $name}}
{
	{{if ne .Ret "void"}}return {{end}}(*(Get{{$name}}Interface(this)->{{.Name}}))({{.ArgCallList}});
}

{{end}}static void {{.Name}}_default_init({{.Name}}Interface *iface)
{
}

void verify{{.Name}}Impl(char *typename, {{.Name}}Interface *iface)
{
{{$name := .Name}}{{range .Methods}}	if (iface->{{.Name}} == NULL)
		g_error("BUG: type %s missing implementation of {{$name}} method {{.Name}}()", typename);
{{end}}}
{{end}}{{end}}`

func gencfile(f File, filename string) {
	of, err := os.Create(filename)
	if err != nil {
		die("error creating %s: %v", filename, err)
	}
	defer of.Close()

	t := template.Must(template.New("cfile").Parse(cfileTemplate))
	err = t.Execute(of, f)
	if err != nil {
		die("error generating .c file: %v", err)
	}
}

const stubTemplate = `{{$tn := .TypeName}}struct {{$tn}}Priv {
};

G_DEFINE_TYPE_WITH_CODE({{$tn}}, {{$tn}}, G_TYPE_OBJECT,{{range .Interfaces}}
	G_IMPLEMENT_INTERFACE({{.Name}}Type, {{$tn}}_{{.Name}}_init){{end}})

static void {{$tn}}_init({{$tn}} *this)
{
	this->priv = G_TYPE_INSTANCE_GET_PRIVATE(this, {{$tn}}Type, struct {{$tn}}Priv);
}

static void {{$tn}}_dispose(GObject *obj)
{
	G_OBJECT_CLASS({{$tn}}_parent_class)->dispose(obj);
}

static void {{$tn}}_finalize(GObject *obj)
{
	G_OBJECT_CLASS({{$tn}}_parent_class)->finalize(obj);
}
{{range .Interfaces}}{{range .Methods}}
static {{.GlobalDefn $tn}}
{
	// TODO
{{if ne .Ret "void"}}	// TODO return something
{{end}}}
{{end}}
static void {{$tn}}_{{.Name}}_init({{.Name}}Interface *iface)
{
{{range .Methods}}	iface->{{.Name}} = {{$tn}}{{.Name}};
{{end}}	verify{{.Name}}Impl("{{$tn}}", iface);
}
{{end}}
static void {{$tn}}_class_init({{$tn}}Class *class)
{
	g_type_class_add_private(class, sizeof (struct {{$tn}}Priv));

	G_OBJECT_CLASS(class)->dispose = {{$tn}}_dispose;
	G_OBJECT_CLASS(class)->finalize = {{$tn}}_finalize;
}
`

func genstub(f File, typename string, filename string) {
	of, err := os.Create(filename)
	if err != nil {
		die("error creating %s: %v", filename, err)
	}
	defer of.Close()

	t := template.Must(template.New("stub").Parse(stubTemplate))
	err = t.Execute(of, struct {
		TypeName	string
		Interfaces		[]*Interface
	}{
		TypeName:	typename,
		Interfaces:	ifacesOnly(f),
	})
	if err != nil {
		die("error generating .c file: %v", err)
	}
}

func main() {
	if len(os.Args) != 4 {
		die("usage: %s ifacefile type outfile", os.Args[0])
	}

	f := readfile(os.Args[1])
	outfile := os.Args[3]

	s := os.Args[2]
	switch {
	case s == "hfile":
		genhfile(f, outfile)
	case s == "cfile":
		gencfile(f, outfile)
	case strings.HasPrefix(s, "stub"):
		s = s[4:]
		genstub(f, s, outfile)
	default:
		die("unknown output type %q", s)
	}
}
<commit_msg>More iface2cdecl.go refinement.<commit_after>// 24 march 2015
package main

// TODO
// - should stub generator should provide a way to rename 'this'?

import (
	"fmt"
	"os"
	"bufio"
	"strings"
	"text/template"
)

type Method struct {
	Ret			string
	Name		string
	ArgTypes		[]string
	ArgNames	[]string
	ArgsFull		[]string
}

type Interface struct {
	IsRaw	bool
	Raw		string
	Name	string
	Methods	[]*Method
}

type File []*Interface

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(1)
}

func (m *Method) loadargs(raw []string) {
	m.ArgTypes = make([]string, 0, len(raw) / 2)
	m.ArgNames = make([]string, 0, len(raw) / 2)
	m.ArgsFull = make([]string, 0, len(raw) / 2)
	for i := 0; i < len(raw); i += 2 {
		ty := raw[i]
		name := raw[i + 1]
		if ty[len(ty) - 1] == '*' {
			ty = ty[:len(ty) - 1] + " *"
			m.ArgsFull = append(m.ArgsFull, ty + name)
		} else {
			m.ArgsFull = append(m.ArgsFull, ty + " " + name)
		}
		m.ArgTypes = append(m.ArgTypes, ty)
		m.ArgNames = append(m.ArgNames, name)
	}
}

func readfile(filename string) (f File) {
	var curiface *Interface

	infile, err := os.Open(filename)
	if err != nil {
		die("error opening %s: %v", filename, err)
	}
	defer infile.Close()

	scanner := bufio.NewScanner(infile)
	for scanner.Scan() {
		s := scanner.Text()
		if s == "" {
			continue
		}
		if s[0] == 'r' {		// raw line
			f = append(f, &Interface{
				IsRaw:	true,
				Raw:		s[1:],
			})
			continue
		}
		p := strings.Split(strings.TrimSpace(s), " ")
		if p[0][0] == '#' {	// comment
			continue
		}
		if p[0] == "i" {		// new interface
			if curiface != nil {
				f = append(f, curiface)
			}
			curiface = &Interface{
				Name:	p[1],
			}
			continue
		}
		if p[0] == "m" {		// method
			m := &Method{
				Ret:		p[1],
				Name:	p[2],
			}
			m.loadargs(p[3:])
			curiface.Methods = append(curiface.Methods, m)
			continue
		}
		die("unknown line %q\n", s)
	}
	if err := scanner.Err(); err != nil {
		die("error reading %s: %v", filename, err)
	}

	if curiface != nil {
		f = append(f, curiface)
	}
	return f
}

const banner = "// generated by tools/iface2cdecl.go; do not edit"

func (m *Method) decl(curiface string, name string, this bool, args []string) string {
	var s string

	if m.Ret[len(m.Ret) - 1] == '*' {
		s = m.Ret[:len(m.Ret) - 1] + " *"
	} else {
		s = m.Ret + " "
	}
	s += name
	s += "(" + curiface + " *"
	if this {
		s += "this"
	}
	for _, a := range args {
		s += ", " + a
	}
	s += ")"
	return s
}

func (m *Method) VtableDecl(curiface string) string {
	return m.decl(curiface, "(*" + m.Name + ")", false, m.ArgTypes)
}

func (m *Method) GlobalDecl(curiface string) string {
	return m.decl(curiface, curiface + m.Name, false, m.ArgTypes)
}

func (m *Method) GlobalDefn(curiface string) string {
	return m.decl(curiface, curiface + m.Name, true, m.ArgsFull)
}

// TODO split the for loop into its own function?
func (m *Method) ArgCallList() string {
	s := "this"
	for _, a := range m.ArgNames {
		s += ", " + a
	}
	return s
}

func ifacesOnly(f File) []*Interface {
	ifaces := make([]*Interface, 0, len(f))
	for _, i := range f {
		if !i.IsRaw {
			ifaces = append(ifaces, i)
		}
	}
	return ifaces
}

const hfileTemplate = banner + `
{{range .}}
#define {{.Name}}Type ({{.Name}}_get_type())
#define {{.Name}}(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), {{.Name}}Type, {{.Name}}))
#define Is{{.Name}}(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), {{.Name}}Type))
#define Get{{.Name}}Interface(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), {{.Name}}Type, {{.Name}}Interface))
typedef struct {{.Name}} {{.Name}};
typedef struct {{.Name}}Interface {{.Name}}Interface;
struct {{.Name}}Interface {
	GTypeInterface parent_iface;
{{$name := .Name}}{{range .Methods}}	{{.VtableDecl $name}};
{{end}}};
extern GType {{.Name}}_get_type(void);
extern void verify{{.Name}}Impl(char *, {{.Name}}Interface *);
{{$name := .Name}}{{range .Methods}}extern {{.GlobalDecl $name}};
{{end}}{{end}}`

func genhfile(f File, filename string) {
	of, err := os.Create(filename)
	if err != nil {
		die("error creating %s: %v", filename, err)
	}
	defer of.Close()

	t := template.Must(template.New("hfile").Parse(hfileTemplate))
	err = t.Execute(of, ifacesOnly(f))
	if err != nil {
		die("error generating .h file: %v", err)
	}
}

const cfileTemplate = banner + `
{{range .}}{{if .IsRaw}}{{.Raw}}
{{else}}
// {{.Name}}

G_DEFINE_INTERFACE({{.Name}}, {{.Name}}, G_TYPE_OBJECT)

{{$name := .Name}}{{range .Methods}}{{.GlobalDefn $name}}
{
	{{if ne .Ret "void"}}return {{end}}(*(Get{{$name}}Interface(this)->{{.Name}}))({{.ArgCallList}});
}

{{end}}static void {{.Name}}_default_init({{.Name}}Interface *iface)
{
}

void verify{{.Name}}Impl(char *typename, {{.Name}}Interface *iface)
{
{{$name := .Name}}{{range .Methods}}	if (iface->{{.Name}} == NULL)
		g_error("BUG: type %s missing implementation of {{$name}} method {{.Name}}()", typename);
{{end}}}
{{end}}{{end}}`

func gencfile(f File, filename string) {
	of, err := os.Create(filename)
	if err != nil {
		die("error creating %s: %v", filename, err)
	}
	defer of.Close()

	t := template.Must(template.New("cfile").Parse(cfileTemplate))
	err = t.Execute(of, f)
	if err != nil {
		die("error generating .c file: %v", err)
	}
}

const stubTemplate = `{{$tn := .TypeName}}struct {{$tn}}Priv {
};

G_DEFINE_TYPE_WITH_CODE({{$tn}}, {{$tn}}, G_TYPE_OBJECT,{{range .Interfaces}}
	G_IMPLEMENT_INTERFACE({{.Name}}Type, {{$tn}}_{{.Name}}_init){{end}})

static void {{$tn}}_init({{$tn}} *this)
{
	this->priv = G_TYPE_INSTANCE_GET_PRIVATE(this, {{$tn}}Type, struct {{$tn}}Priv);
}

static void {{$tn}}_dispose(GObject *obj)
{
	G_OBJECT_CLASS({{$tn}}_parent_class)->dispose(obj);
}

static void {{$tn}}_finalize(GObject *obj)
{
	G_OBJECT_CLASS({{$tn}}_parent_class)->finalize(obj);
}
{{range .Interfaces}}{{range .Methods}}
static {{.GlobalDefn $tn}}
{
	// TODO
{{if ne .Ret "void"}}	// TODO return something
{{end}}}
{{end}}
static void {{$tn}}_{{.Name}}_init({{.Name}}Interface *iface)
{
{{range .Methods}}	iface->{{.Name}} = {{$tn}}{{.Name}};
{{end}}	verify{{.Name}}Impl("{{$tn}}", iface);
}
{{end}}
static void {{$tn}}_class_init({{$tn}}Class *class)
{
	g_type_class_add_private(class, sizeof (struct {{$tn}}Priv));

	G_OBJECT_CLASS(class)->dispose = {{$tn}}_dispose;
	G_OBJECT_CLASS(class)->finalize = {{$tn}}_finalize;
}
`

func genstub(f File, typename string, filename string) {
	of, err := os.Create(filename)
	if err != nil {
		die("error creating %s: %v", filename, err)
	}
	defer of.Close()

	t := template.Must(template.New("stub").Parse(stubTemplate))
	err = t.Execute(of, struct {
		TypeName	string
		Interfaces		[]*Interface
	}{
		TypeName:	typename,
		Interfaces:	ifacesOnly(f),
	})
	if err != nil {
		die("error generating .c file: %v", err)
	}
}

func main() {
	if len(os.Args) != 4 {
		die("usage: %s ifacefile type outfile", os.Args[0])
	}

	f := readfile(os.Args[1])
	outfile := os.Args[3]

	s := os.Args[2]
	switch {
	case s == "hfile":
		genhfile(f, outfile)
	case s == "cfile":
		gencfile(f, outfile)
	case strings.HasPrefix(s, "stub"):
		s = s[4:]
		genstub(f, s, outfile)
	default:
		die("unknown output type %q", s)
	}
}
<|endoftext|>


<commit_before>package comments

import (
	"errors"
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/parkr/auto-reply/Godeps/_workspace/src/github.com/google/go-github/github"
)

var (
	mergeCommentRegexp = regexp.MustCompile("@[a-zA-Z-_]+: (merge|:shipit:|:ship:)( \\+([a-zA-Z-_ ]+))?")

	HandlerMergeAndLabel = func(client *github.Client, event github.IssueCommentEvent) error {
		// Is this a pull request?
		if !isPullRequest(event) {
			return errors.New("not a pull request")
		}

		var changeSectionLabel string
		isReq, labelFromComment := parseMergeRequestComment(*event.Comment.Body)

		// Is It a merge request comment?
		if !isReq {
			return errors.New("not a merge request comment")
		}

		owner, repo, number := *event.Repo.Owner.Login, *event.Repo.Name, *event.Issue.Number

		// Does the user have merge/label abilities?
		if !isAuthorizedCommenter(event.Comment.User) {
			return errors.New("commenter isn't allowed to merge")
		}

		// Should it be labeled?
		if labelFromComment != "" {
			// Apply label
			changeSectionLabel = sectionForLabel(labelFromComment)
		} else {
			// Get changeSectionLabel from issue labels!
			labels, _, err := client.Issues.ListLabelsForMilestone(owner, repo, number, nil)
			if err != nil {
				return err
			}
			fmt.Printf("labels from GitHub = %v\n", labels)
			changeSectionLabel = sectionForLabel(selectSectionLabel(labels))
		}
		fmt.Printf("changeSectionLabel = '%s'\n", changeSectionLabel)

		// Merge
		commitMsg := fmt.Sprintf("Merge pull request %v", number)
		_, _, mergeErr := client.PullRequests.Merge(owner, repo, number, commitMsg)
		if mergeErr != nil {
			fmt.Printf("comments: error merging %v\n", mergeErr)
			return mergeErr
		}

		// Delete branch
		// ref := fmt.Sprintf("heads/%s", branch)
		// res, deleteBranchErr := client.Git.DeleteRef(owner, repo, ref)

		// Read History.markdown, add line to appropriate change section
		historyFileContents := getHistoryContents(client, owner, repo)
		log.Println(historyFileContents)

		// Commit change to History.markdown

		return nil
	}
)

func isAuthorizedCommenter(user *github.User) bool {
	return *user.Login == "parkr"
}

func parseMergeRequestComment(commentBody string) (bool, string) {
	matches := mergeCommentRegexp.FindAllStringSubmatch(commentBody, -1)
	if matches == nil || matches[0] == nil {
		return false, ""
	}

	var label string
	if len(matches[0]) >= 4 {
		if labelFromComment := matches[0][3]; labelFromComment != "" {
			label = downcaseAndHyphenize(labelFromComment)
		}
	}

	return true, normalizeLabel(label)
}

func downcaseAndHyphenize(label string) string {
	return strings.Replace(strings.ToLower(label), " ", "-", -1)
}

func normalizeLabel(label string) string {
	if strings.HasPrefix(label, "major") {
		return "major-enhancements"
	}

	if strings.HasPrefix(label, "minor") {
		return "minor-enhancements"
	}

	if strings.HasPrefix(label, "bug") {
		return "bug-fixes"
	}

	if strings.HasPrefix(label, "development") {
		return "development-fixes"
	}

	return label
}

func sectionForLabel(label string) string {
	switch label {
	case "major-enhancements":
		return "Major Enhancements"
	case "minor-enhancements":
		return "Minor Enhancements"
	case "bug-fixes":
		return "Bug Fixes"
	case "development-fixes":
		return "Development Fixes"
	default:
		return label
	}
}

func selectSectionLabel(labels []github.Label) string {
	for _, label := range labels {
		if sectionForLabel(*label.Name) != *label.Name {
			return *label.Name
		}
	}
	return ""
}

func containsChangeLabel(commentBody string) bool {
	_, labelFromComment := parseMergeRequestComment(commentBody)
	return labelFromComment != ""
}

func getHistoryContents(client *github.Client, owner, repo string) string {
	content, _, _, err := client.Repositories.GetContents(owner, repo, "History.markdown", nil)
	if err != nil {
		fmt.Printf("comments: error getting History.markdown %v\n", err)
		return ""
	}
	return *content.Content
}
<commit_msg>comments: print issuecommentevent<commit_after>package comments

import (
	"errors"
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/parkr/auto-reply/Godeps/_workspace/src/github.com/google/go-github/github"
)

var (
	mergeCommentRegexp = regexp.MustCompile("@[a-zA-Z-_]+: (merge|:shipit:|:ship:)( \\+([a-zA-Z-_ ]+))?")

	HandlerMergeAndLabel = func(client *github.Client, event github.IssueCommentEvent) error {
		// Is this a pull request?
		if !isPullRequest(event) {
			return errors.New("not a pull request")
		}

		var changeSectionLabel string
		isReq, labelFromComment := parseMergeRequestComment(*event.Comment.Body)

		// Is It a merge request comment?
		if !isReq {
			return errors.New("not a merge request comment")
		}

		log.Println(event)

		owner, repo, number := *event.Repo.Owner.Login, *event.Repo.Name, *event.Issue.Number

		// Does the user have merge/label abilities?
		if !isAuthorizedCommenter(event.Comment.User) {
			return errors.New("commenter isn't allowed to merge")
		}

		// Should it be labeled?
		if labelFromComment != "" {
			// Apply label
			changeSectionLabel = sectionForLabel(labelFromComment)
		} else {
			// Get changeSectionLabel from issue labels!
			labels, _, err := client.Issues.ListLabelsForMilestone(owner, repo, number, nil)
			if err != nil {
				return err
			}
			fmt.Printf("labels from GitHub = %v\n", labels)
			changeSectionLabel = sectionForLabel(selectSectionLabel(labels))
		}
		fmt.Printf("changeSectionLabel = '%s'\n", changeSectionLabel)

		// Merge
		commitMsg := fmt.Sprintf("Merge pull request %v", number)
		_, _, mergeErr := client.PullRequests.Merge(owner, repo, number, commitMsg)
		if mergeErr != nil {
			fmt.Printf("comments: error merging %v\n", mergeErr)
			return mergeErr
		}

		// Delete branch
		// ref := fmt.Sprintf("heads/%s", branch)
		// res, deleteBranchErr := client.Git.DeleteRef(owner, repo, ref)

		// Read History.markdown, add line to appropriate change section
		historyFileContents := getHistoryContents(client, owner, repo)
		log.Println(historyFileContents)

		// Commit change to History.markdown

		return nil
	}
)

func isAuthorizedCommenter(user *github.User) bool {
	return *user.Login == "parkr"
}

func parseMergeRequestComment(commentBody string) (bool, string) {
	matches := mergeCommentRegexp.FindAllStringSubmatch(commentBody, -1)
	if matches == nil || matches[0] == nil {
		return false, ""
	}

	var label string
	if len(matches[0]) >= 4 {
		if labelFromComment := matches[0][3]; labelFromComment != "" {
			label = downcaseAndHyphenize(labelFromComment)
		}
	}

	return true, normalizeLabel(label)
}

func downcaseAndHyphenize(label string) string {
	return strings.Replace(strings.ToLower(label), " ", "-", -1)
}

func normalizeLabel(label string) string {
	if strings.HasPrefix(label, "major") {
		return "major-enhancements"
	}

	if strings.HasPrefix(label, "minor") {
		return "minor-enhancements"
	}

	if strings.HasPrefix(label, "bug") {
		return "bug-fixes"
	}

	if strings.HasPrefix(label, "development") {
		return "development-fixes"
	}

	return label
}

func sectionForLabel(label string) string {
	switch label {
	case "major-enhancements":
		return "Major Enhancements"
	case "minor-enhancements":
		return "Minor Enhancements"
	case "bug-fixes":
		return "Bug Fixes"
	case "development-fixes":
		return "Development Fixes"
	default:
		return label
	}
}

func selectSectionLabel(labels []github.Label) string {
	for _, label := range labels {
		if sectionForLabel(*label.Name) != *label.Name {
			return *label.Name
		}
	}
	return ""
}

func containsChangeLabel(commentBody string) bool {
	_, labelFromComment := parseMergeRequestComment(commentBody)
	return labelFromComment != ""
}

func getHistoryContents(client *github.Client, owner, repo string) string {
	content, _, _, err := client.Repositories.GetContents(owner, repo, "History.markdown", nil)
	if err != nil {
		fmt.Printf("comments: error getting History.markdown %v\n", err)
		return ""
	}
	return *content.Content
}
<|endoftext|>


<commit_before><commit_msg>"Jeff: Come to the dark side... together we will rule the galaxy."<commit_after><|endoftext|>


<commit_before><commit_msg>"Jeff: Give yourself to the Dark Side. It is the only way you can save your friends."<commit_after><|endoftext|>


<commit_before>package print_store

import (
	"encoding/json"
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/cloudfoundry-incubator/runtime-schema/models"

	"github.com/cloudfoundry-incubator/veritas/say"
	"github.com/cloudfoundry-incubator/veritas/veritas_models"
)

func PrintDistribution(tasks bool, lrps bool, clear bool, f io.Reader) error {
	decoder := json.NewDecoder(f)
	var dump veritas_models.StoreDump
	err := decoder.Decode(&dump)
	if err != nil {
		return err
	}

	if clear {
		say.Clear()
	}

	printDistribution(dump, tasks, lrps)

	return nil
}

func printDistribution(dump veritas_models.StoreDump, includeTasks bool, includeLRPS bool) {
	executorIDs := []string{}
	for _, executorPresence := range dump.Services.Executors {
		executorIDs = append(executorIDs, executorPresence.ExecutorID)
	}

	sort.Strings(executorIDs)

	nTasks := map[string]int{}
	nLRPsStarting := map[string]int{}
	nLRPsRunning := map[string]int{}

	for _, tasks := range dump.Tasks {
		for _, task := range tasks {
			nTasks[task.ExecutorID]++
		}
	}

	for _, lrp := range dump.LRPS {
		for _, actuals := range lrp.ActualLRPsByIndex {
			for _, actual := range actuals {
				if actual.State == models.ActualLRPStateStarting {
					nLRPsStarting[actual.ExecutorID]++
				} else {
					nLRPsRunning[actual.ExecutorID]++
				}
			}
		}
	}

	say.Println(0, "Distribution")
	for _, executorID := range executorIDs {
		numTasks := nTasks[executorID]
		numLRPs := nLRPsStarting[executorID] + nLRPsRunning[executorID]
		var content string
		if numTasks == 0 && numLRPs == 0 {
			content = say.Red("Empty")
		} else {
			content = fmt.Sprintf("%s%s", say.Yellow(strings.Repeat("•", nTasks[executorID])), say.Green(strings.Repeat("•", nLRPsRunning[executorID])), say.Gray(strings.Repeat("•", nLRPsStarting[executorID])))
		}
		say.Println(0, "%12s: %s", executorID, content)
	}
}
<commit_msg>tweak distribution formatting<commit_after>package print_store

import (
	"encoding/json"
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/cloudfoundry-incubator/runtime-schema/models"

	"github.com/cloudfoundry-incubator/veritas/say"
	"github.com/cloudfoundry-incubator/veritas/veritas_models"
)

func PrintDistribution(tasks bool, lrps bool, clear bool, f io.Reader) error {
	decoder := json.NewDecoder(f)
	var dump veritas_models.StoreDump
	err := decoder.Decode(&dump)
	if err != nil {
		return err
	}

	printDistribution(dump, tasks, lrps, clear)

	return nil
}

func printDistribution(dump veritas_models.StoreDump, includeTasks bool, includeLRPS bool, clear bool) {
	executorIDs := []string{}
	for _, executorPresence := range dump.Services.Executors {
		executorIDs = append(executorIDs, executorPresence.ExecutorID)
	}

	sort.Strings(executorIDs)

	nTasks := map[string]int{}
	nLRPsStarting := map[string]int{}
	nLRPsRunning := map[string]int{}

	for _, tasks := range dump.Tasks {
		for _, task := range tasks {
			nTasks[task.ExecutorID]++
		}
	}

	for _, lrp := range dump.LRPS {
		for _, actuals := range lrp.ActualLRPsByIndex {
			for _, actual := range actuals {
				if actual.State == models.ActualLRPStateStarting {
					nLRPsStarting[actual.ExecutorID]++
				} else {
					nLRPsRunning[actual.ExecutorID]++
				}
			}
		}
	}

	if clear {
		say.Clear()
	}

	say.Println(0, "Distribution")
	for _, executorID := range executorIDs {
		numTasks := nTasks[executorID]
		numLRPs := nLRPsStarting[executorID] + nLRPsRunning[executorID]
		var content string
		if numTasks == 0 && numLRPs == 0 {
			content = say.Red("Empty")
		} else {
			content = fmt.Sprintf("%s%s", say.Yellow(strings.Repeat("•", nTasks[executorID])), say.Green(strings.Repeat("•", nLRPsRunning[executorID])), say.Gray(strings.Repeat("•", nLRPsStarting[executorID])))
		}
		say.Println(0, "%12s: %s", executorID, content)
	}
}
<|endoftext|>


<commit_before>// Advertise network services via multicast DNS
package mdns

import (
	"log"
	"net"
	"os"

	dns "github.com/miekg/godns"
)

var (
	ipv4mcastaddr = &net.UDPAddr{
		IP:   net.ParseIP("224.0.0.251"),
		Port: 5353,
	}

	ipv6mcastaddr = &net.UDPAddr{
		IP:   net.ParseIP("ff02::fb"),
		Port: 5353,
	}
	local *zone // the local mdns zone
)

func init() {
	local = &zone{
		entries: make(map[string]entries),
		add:     make(chan *entry, 16),
		queries: make(chan *query, 16),
	}
	go local.mainloop()
	if err := local.listen(ipv4mcastaddr); err != nil {
		log.Fatalf("Failed to listen %s: %s", ipv4mcastaddr, err)
	}
	if err := local.listen(ipv6mcastaddr); err != nil {
		log.Printf("Failed to listen %s: %s", ipv6mcastaddr, err)
	}
}

// Add an A record. fqdn should be fully qualified, including local.
func PublishA(fqdn string, ttl int, ip net.IP) {
	local.add <- &entry{
		RR: &dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			A: ip,
		},
	}
}

// Publish a PTR record, fqdn should be fully qualified, including local.
func PublishPTR(fqdn string, ttl int, target string) {
	local.add <- &entry{
		RR: &dns.RR_PTR{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypePTR,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			Ptr: target,
		},
	}
}

// Publish a SRV record, fqdn and target should be fully qualified, including local.
func PublishSRV(fqdn string, ttl int, target string, port int) {
	local.add <- &entry{
		RR: &dns.RR_SRV{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypeSRV,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			Target: target,
			Port:   uint16(port),
		},
	}
}

// Publish a TXT record, fqdn should be fully qualified, including local.
func PublishTXT(fqdn string, ttl int, txt string) {
	local.add <- &entry{
		RR: &dns.RR_TXT{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypeTXT,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			Txt: txt,
		},
	}
}

type entry struct {
	dns.RR
}

func (e *entry) fqdn() string {
	return e.Header().Name
}

type query struct {
	dns.Question
	result chan *entry
}

type entries []*entry

func (e entries) contains(entry *entry) bool {
	for _, ee := range e {
		if equals(entry, ee) {
			return true
		}
	}
	return false
}

type zone struct {
	entries map[string]entries
	add     chan *entry // add entries to zone
	queries chan *query // query exsting entries in zone
}

func (z *zone) mainloop() {
	for {
		select {
		case entry := <-z.add:
			if !z.entries[entry.fqdn()].contains(entry) {
				z.entries[entry.fqdn()] = append(z.entries[entry.fqdn()], entry)
			}
		case q := <-z.queries:
			for _, entry := range z.entries[q.Question.Name] {
				if q.matches(entry) {
					q.result <- entry
				}
			}
			close(q.result)
		}
	}
}

func (z *zone) query(q dns.Question) (entries []*entry) {
	res := make(chan *entry, 16)
	z.queries <- &query{q, res}
	for e := range res {
		entries = append(entries, e)
	}
	return
}

func (q *query) matches(entry *entry) bool {
	return q.Question.Qtype == dns.TypeANY || q.Question.Qtype == entry.RR.Header().Rrtype
}

func equals(this, that *entry) bool {
	if _, ok := this.RR.(*dns.RR_ANY); ok {
		return true // *RR_ANY matches anything
	}
	if _, ok := that.RR.(*dns.RR_ANY); ok {
		return true // *RR_ANY matches all
	}
	return false
}

type connector struct {
	*net.UDPAddr
	*net.UDPConn
	*zone
}

func (z *zone) listen(addr *net.UDPAddr) os.Error {
	conn, err := openSocket(addr)
	if err != nil {
		return err
	}
	if err := conn.JoinGroup(nil, addr.IP); err != nil {
		return err
	}
	c := &connector{
		UDPAddr: addr,
		UDPConn: conn,
		zone:    z,
	}
	go c.mainloop()
	return nil
}

func openSocket(addr *net.UDPAddr) (*net.UDPConn, os.Error) {
	switch addr.IP.To4() {
	case nil:
		return net.ListenUDP("udp6", &net.UDPAddr{
			IP:   net.IPv6zero,
			Port: addr.Port,
		})
	default:
		return net.ListenUDP("udp4", &net.UDPAddr{
			IP:   net.IPv4zero,
			Port: addr.Port,
		})
	}
	panic("unreachable")
}

func (c *connector) mainloop() {
	in := make(chan struct {
		*dns.Msg
		*net.UDPAddr
	}, 32)
	go func() {
		for {
			msg, addr, err := c.readMessage()
			if err != nil {
				log.Fatalf("Cound not read from %s: %s", c.UDPConn, err)
			}
			if msg.IsQuestion() {
				in <- struct {
					*dns.Msg
					*net.UDPAddr
				}{msg, addr}
			}
		}
	}()
	for {
		msg := <-in
		msg.MsgHdr.Response = true // convert question to response
		for _, result := range c.query(msg.Question) {
			msg.Answer = append(msg.Answer, result.RR)
		}
		msg.Extra = append(msg.Extra, c.findExtra(msg.Answer...)...)
		if len(msg.Answer) > 0 {
			if err := c.writeMessage(msg.Msg); err != nil {
				log.Fatalf("Cannot send: %s", err)
			}
		}
	}
}

func (c *connector) query(qs []dns.Question) (results []*entry) {
	for _, q := range qs {
		results = append(results, c.zone.query(q)...)
	}
	return
}

// recursively probe for related records
func (c *connector) findExtra(r ...dns.RR) (extra []dns.RR) {
	for _, rr := range r {
		var q dns.Question
		switch rr := rr.(type) {
		case *dns.RR_PTR:
			q = dns.Question{
				Name:   rr.Ptr,
				Qtype:  dns.TypeANY,
				Qclass: dns.ClassINET,
			}
		case *dns.RR_SRV:
			q = dns.Question{
				Name:   rr.Target,
				Qtype:  dns.TypeA,
				Qclass: dns.ClassINET,
			}
		default:
			continue
		}
		res := c.zone.query(q)
		if len(res) > 0 {
			for _, entry := range res {
				extra = append(append(extra, entry.RR), c.findExtra(entry.RR)...)
			}
		}
	}
	return
}

// encode an mdns msg and broadcast it on the wire
func (c *connector) writeMessage(msg *dns.Msg) (err os.Error) {
	if buf, ok := msg.Pack(); ok {
		_, err = c.WriteToUDP(buf, c.UDPAddr)
	}
	return
}

// consume an mdns packet from the wire and decode it
func (c *connector) readMessage() (*dns.Msg, *net.UDPAddr, os.Error) {
	buf := make([]byte, 1500)
	read, addr, err := c.ReadFromUDP(buf)
	if err != nil {
		return nil, nil, err
	}
	if msg := new(dns.Msg); msg.Unpack(buf[:read]) {
		return msg, addr, nil
	}
	return nil, addr, os.NewError("Unable to unpack buffer")
}
<commit_msg>Don't exit when receiving a dud packet<commit_after>// Advertise network services via multicast DNS
package mdns

import (
	"log"
	"net"
	"os"

	dns "github.com/miekg/godns"
)

var (
	ipv4mcastaddr = &net.UDPAddr{
		IP:   net.ParseIP("224.0.0.251"),
		Port: 5353,
	}

	ipv6mcastaddr = &net.UDPAddr{
		IP:   net.ParseIP("ff02::fb"),
		Port: 5353,
	}
	local *zone // the local mdns zone
)

func init() {
	local = &zone{
		entries: make(map[string]entries),
		add:     make(chan *entry, 16),
		queries: make(chan *query, 16),
	}
	go local.mainloop()
	if err := local.listen(ipv4mcastaddr); err != nil {
		log.Fatalf("Failed to listen %s: %s", ipv4mcastaddr, err)
	}
	if err := local.listen(ipv6mcastaddr); err != nil {
		log.Printf("Failed to listen %s: %s", ipv6mcastaddr, err)
	}
}

// Add an A record. fqdn should be fully qualified, including local.
func PublishA(fqdn string, ttl int, ip net.IP) {
	local.add <- &entry{
		RR: &dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			A: ip,
		},
	}
}

// Publish a PTR record, fqdn should be fully qualified, including local.
func PublishPTR(fqdn string, ttl int, target string) {
	local.add <- &entry{
		RR: &dns.RR_PTR{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypePTR,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			Ptr: target,
		},
	}
}

// Publish a SRV record, fqdn and target should be fully qualified, including local.
func PublishSRV(fqdn string, ttl int, target string, port int) {
	local.add <- &entry{
		RR: &dns.RR_SRV{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypeSRV,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			Target: target,
			Port:   uint16(port),
		},
	}
}

// Publish a TXT record, fqdn should be fully qualified, including local.
func PublishTXT(fqdn string, ttl int, txt string) {
	local.add <- &entry{
		RR: &dns.RR_TXT{
			Hdr: dns.RR_Header{
				Name:   fqdn,
				Rrtype: dns.TypeTXT,
				Class:  dns.ClassINET,
				Ttl:    uint32(ttl),
			},
			Txt: txt,
		},
	}
}

type entry struct {
	dns.RR
}

func (e *entry) fqdn() string {
	return e.Header().Name
}

type query struct {
	dns.Question
	result chan *entry
}

type entries []*entry

func (e entries) contains(entry *entry) bool {
	for _, ee := range e {
		if equals(entry, ee) {
			return true
		}
	}
	return false
}

type zone struct {
	entries map[string]entries
	add     chan *entry // add entries to zone
	queries chan *query // query exsting entries in zone
}

func (z *zone) mainloop() {
	for {
		select {
		case entry := <-z.add:
			if !z.entries[entry.fqdn()].contains(entry) {
				z.entries[entry.fqdn()] = append(z.entries[entry.fqdn()], entry)
			}
		case q := <-z.queries:
			for _, entry := range z.entries[q.Question.Name] {
				if q.matches(entry) {
					q.result <- entry
				}
			}
			close(q.result)
		}
	}
}

func (z *zone) query(q dns.Question) (entries []*entry) {
	res := make(chan *entry, 16)
	z.queries <- &query{q, res}
	for e := range res {
		entries = append(entries, e)
	}
	return
}

func (q *query) matches(entry *entry) bool {
	return q.Question.Qtype == dns.TypeANY || q.Question.Qtype == entry.RR.Header().Rrtype
}

func equals(this, that *entry) bool {
	if _, ok := this.RR.(*dns.RR_ANY); ok {
		return true // *RR_ANY matches anything
	}
	if _, ok := that.RR.(*dns.RR_ANY); ok {
		return true // *RR_ANY matches all
	}
	return false
}

type connector struct {
	*net.UDPAddr
	*net.UDPConn
	*zone
}

func (z *zone) listen(addr *net.UDPAddr) os.Error {
	conn, err := openSocket(addr)
	if err != nil {
		return err
	}
	if err := conn.JoinGroup(nil, addr.IP); err != nil {
		return err
	}
	c := &connector{
		UDPAddr: addr,
		UDPConn: conn,
		zone:    z,
	}
	go c.mainloop()
	return nil
}

func openSocket(addr *net.UDPAddr) (*net.UDPConn, os.Error) {
	switch addr.IP.To4() {
	case nil:
		return net.ListenUDP("udp6", &net.UDPAddr{
			IP:   net.IPv6zero,
			Port: addr.Port,
		})
	default:
		return net.ListenUDP("udp4", &net.UDPAddr{
			IP:   net.IPv4zero,
			Port: addr.Port,
		})
	}
	panic("unreachable")
}

func (c *connector) mainloop() {
	in := make(chan struct {
		*dns.Msg
		*net.UDPAddr
	}, 32)
	go func() {
		for {
			msg, addr, err := c.readMessage()
			if err != nil {
				// log dud packets
				log.Printf("Cound not read from %s: %s", c.UDPConn, err)
			}
			if msg.IsQuestion() {
				in <- struct {
					*dns.Msg
					*net.UDPAddr
				}{msg, addr}
			}
		}
	}()
	for {
		msg := <-in
		msg.MsgHdr.Response = true // convert question to response
		for _, result := range c.query(msg.Question) {
			msg.Answer = append(msg.Answer, result.RR)
		}
		msg.Extra = append(msg.Extra, c.findExtra(msg.Answer...)...)
		if len(msg.Answer) > 0 {
			if err := c.writeMessage(msg.Msg); err != nil {
				log.Fatalf("Cannot send: %s", err)
			}
		}
	}
}

func (c *connector) query(qs []dns.Question) (results []*entry) {
	for _, q := range qs {
		results = append(results, c.zone.query(q)...)
	}
	return
}

// recursively probe for related records
func (c *connector) findExtra(r ...dns.RR) (extra []dns.RR) {
	for _, rr := range r {
		var q dns.Question
		switch rr := rr.(type) {
		case *dns.RR_PTR:
			q = dns.Question{
				Name:   rr.Ptr,
				Qtype:  dns.TypeANY,
				Qclass: dns.ClassINET,
			}
		case *dns.RR_SRV:
			q = dns.Question{
				Name:   rr.Target,
				Qtype:  dns.TypeA,
				Qclass: dns.ClassINET,
			}
		default:
			continue
		}
		res := c.zone.query(q)
		if len(res) > 0 {
			for _, entry := range res {
				extra = append(append(extra, entry.RR), c.findExtra(entry.RR)...)
			}
		}
	}
	return
}

// encode an mdns msg and broadcast it on the wire
func (c *connector) writeMessage(msg *dns.Msg) (err os.Error) {
	if buf, ok := msg.Pack(); ok {
		_, err = c.WriteToUDP(buf, c.UDPAddr)
	}
	return
}

// consume an mdns packet from the wire and decode it
func (c *connector) readMessage() (*dns.Msg, *net.UDPAddr, os.Error) {
	buf := make([]byte, 1500)
	read, addr, err := c.ReadFromUDP(buf)
	if err != nil {
		return nil, nil, err
	}
	if msg := new(dns.Msg); msg.Unpack(buf[:read]) {
		return msg, addr, nil
	}
	return nil, addr, os.NewError("Unable to unpack buffer")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"os"
	"path"
)

func main() {
	flag.Parse()
	configPath := flag.Arg(0)
	_, err := os.Stat(configPath)
	if err != nil && os.IsNotExist(err) {
		log.Fatal("ERROR: Could not find configuration file")
	}

	rules, err := ParseConfig(configPath)
	if err != nil {
		log.Fatalf("ERROR: Could not parse config file: %s", err)
	}
	log.Printf("Successfully loaded configuration file. Number of rules: %d", len(rules))

	dirs := make([]string, 0)
	for _, rule := range rules {
		dirs = append(dirs, rule.Path)
	}

	watcher, err := WatchDirs(dirs)
	if err != nil {
		log.Println("ERROR: Could not start watching directories")
		log.Fatal(err)
	}

	defer func() {
		err = watcher.Stop()
		if err != nil {
			log.Fatal("ERROR: Did not shut down cleanly")
		}
	}()

	queue := Manage(watcher.Events, rules)

	log.Println("Watchgopher is now ready process file events")

	for payload := range queue {
		workOff(payload)
	}
}

func workOff(pl CmdPayload) {
	cmd := pl.Cmd

	if pl.LogOutput {
		outp, err := cmd.StdoutPipe()
		if err != nil {
			log.Printf("%s, ARGS: %s -- ERROR: %s\n", cmd.Path, cmd.Args[1:], err)
			return
		}

		errp, err := cmd.StderrPipe()
		if err != nil {
			log.Printf("%s, ARGS: %s -- ERROR: %s\n", cmd.Path, cmd.Args[1:], err)
			return
		}

		_, filename := path.Split(cmd.Path)
		go pipeToLog(filename, "STDOUT", outp)
		go pipeToLog(filename, "STDERR", errp)
	}

	log.Printf("%s, ARGS: %s -- START\n", cmd.Path, cmd.Args[1:])

	if err := cmd.Start(); err != nil {
		log.Printf("%s, ARGS: %s -- ERROR: %s\n", cmd.Path, cmd.Args[1:], err)
		return
	}

	err := cmd.Wait()
	if err != nil {
		log.Printf("%s, ARGS: %s -- ERROR: %s\n", cmd.Path, cmd.Args[1:], err)
		return
	}
	log.Printf("%s, ARGS: %s -- SUCCESS\n", cmd.Path, cmd.Args[1:])

}

func pipeToLog(filename, prefix string, pipe io.ReadCloser) {
	reader := bufio.NewReader(pipe)
	for {
		line, err := reader.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("[%s %s] Reading Error: %s", filename, prefix, err)
			break
		}
		log.Printf("[%s %s] %s", filename, prefix, line)
	}
}
<commit_msg>DRY up error logging<commit_after>package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
)

func main() {
	flag.Parse()
	configPath := flag.Arg(0)
	_, err := os.Stat(configPath)
	if err != nil && os.IsNotExist(err) {
		log.Fatal("ERROR: Could not find configuration file")
	}

	rules, err := ParseConfig(configPath)
	if err != nil {
		log.Fatalf("ERROR: Could not parse config file: %s", err)
	}
	log.Printf("Successfully loaded configuration file. Number of rules: %d", len(rules))

	dirs := make([]string, 0)
	for _, rule := range rules {
		dirs = append(dirs, rule.Path)
	}

	watcher, err := WatchDirs(dirs)
	if err != nil {
		log.Println("ERROR: Could not start watching directories")
		log.Fatal(err)
	}

	defer func() {
		err = watcher.Stop()
		if err != nil {
			log.Fatal("ERROR: Did not shut down cleanly")
		}
	}()

	queue := Manage(watcher.Events, rules)

	log.Println("Watchgopher is now ready process file events")

	for payload := range queue {
		workOff(payload)
	}
}

func workOff(pl CmdPayload) {
	cmd := pl.Cmd

	if pl.LogOutput {
		outp, err := cmd.StdoutPipe()
		if err != nil {
			logCmdErr(cmd, err)
			return
		}

		errp, err := cmd.StderrPipe()
		if err != nil {
			logCmdErr(cmd, err)
			return
		}

		_, filename := path.Split(cmd.Path)
		go pipeToLog(filename, "STDOUT", outp)
		go pipeToLog(filename, "STDERR", errp)
	}

	log.Printf("%s, ARGS: %s -- START\n", cmd.Path, cmd.Args[1:])

	if err := cmd.Start(); err != nil {
		logCmdErr(cmd, err)
		return
	}

	err := cmd.Wait()
	if err != nil {
		logCmdErr(cmd, err)
		return
	}
	log.Printf("%s, ARGS: %s -- SUCCESS\n", cmd.Path, cmd.Args[1:])

}

func pipeToLog(filename, prefix string, pipe io.ReadCloser) {
	reader := bufio.NewReader(pipe)
	for {
		line, err := reader.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("[%s %s] Reading Error: %s", filename, prefix, err)
			break
		}
		log.Printf("[%s %s] %s", filename, prefix, line)
	}
}

func logCmdErr(cmd *exec.Cmd, err error) {
	log.Printf("%s, ARGS: %s -- ERROR: %s\n", cmd.Path, cmd.Args[1:], err)
}
<|endoftext|>