<commit_before>package service

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sail/internal"
)

var scaleBatch bool
var scaleNumber int
var scaleUsage = "usage: sail services scale [-h] [--number NUMBER] [--batch] <application>/<service>"

// Scale json data arguments
type Scale struct {
	Number int `json:"container_number"`
}

func scaleCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:   "scale",
		Short: scaleUsage,
		Long:  scaleUsage,
		Run:   cmdScale,
	}

	cmd.Flags().BoolVar(&scaleBatch, "batch", false, "do not attach console on start")
	cmd.Flags().IntVar(&scaleNumber, "number", 0, "scale to `number` of containers")

	return cmd
}

func cmdScale(cmd *cobra.Command, args []string) {

	if len(args) != 1 {
		fmt.Fprintln(os.Stderr, startUsage)
		os.Exit(1)
	}

	t := strings.Split(args[0], "/")
	if len(t) != 2 {
		fmt.Fprintln(os.Stderr, startUsage)
		os.Exit(1)
	}

	serviceScale(t[0], t[1], scaleNumber, scaleBatch)
}

func serviceScale(app string, service string, number int, batch bool) {
	path := fmt.Sprintf("/applications/%s/services/%s/scale?stream", app, service)

	args := Scale{
		Number: number,
	}

	data, err := json.Marshal(&args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}

	buffer, _, err := internal.Stream("POST", path, data)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}

	err = internal.DisplayStream(buffer)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}

	serviceStart(app, service, batch)
}
<commit_msg>fix: scale help message was start's<commit_after>package service

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sail/internal"
)

var scaleBatch bool
var scaleNumber int
var scaleUsage = "usage: sail services scale [-h] [--number NUMBER] [--batch] <application>/<service>"

// Scale json data arguments
type Scale struct {
	Number int `json:"container_number"`
}

func scaleCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:   "scale",
		Short: scaleUsage,
		Long:  scaleUsage,
		Run:   cmdScale,
	}

	cmd.Flags().BoolVar(&scaleBatch, "batch", false, "do not attach console on start")
	cmd.Flags().IntVar(&scaleNumber, "number", 0, "scale to `number` of containers")

	return cmd
}

func cmdScale(cmd *cobra.Command, args []string) {

	if len(args) != 1 {
		fmt.Fprintln(os.Stderr, scaleUsage)
		os.Exit(1)
	}

	t := strings.Split(args[0], "/")
	if len(t) != 2 {
		fmt.Fprintln(os.Stderr, startUsage)
		os.Exit(1)
	}

	serviceScale(t[0], t[1], scaleNumber, scaleBatch)
}

func serviceScale(app string, service string, number int, batch bool) {
	path := fmt.Sprintf("/applications/%s/services/%s/scale?stream", app, service)

	args := Scale{
		Number: number,
	}

	data, err := json.Marshal(&args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}

	buffer, _, err := internal.Stream("POST", path, data)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}

	err = internal.DisplayStream(buffer)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}

	serviceStart(app, service, batch)
}
<|endoftext|>


<commit_before>package login

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/thrisp/flotilla"
)

type Tuser struct {
	username string
	active   bool
}

func (u *Tuser) IsAuthenticated() bool {
	return true
}

func (u *Tuser) IsActive() bool {
	return u.active
}

func (u *Tuser) IsAnonymous() bool {
	return false
}

func (u *Tuser) GetId() string {
	return u.username
}

var tusers map[string]*Tuser = map[string]*Tuser{"one": &Tuser{username: "User_One", active: true},
	"two": &Tuser{username: "User_Two"}}

func InMemoryUserLoader(s string) User {
	if u, ok := tusers[s]; ok {
		return u
	}
	return &AnonymousUser{}
}

func PerformRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func basemanager(c ...Configuration) *Manager {
	c = append(c, UserLoader(InMemoryUserLoader))
	l := New(c...)
	return l
}

func testhandler(c *flotilla.Ctx) {
	//passed := true
	//l, _ := c.Call("loginmanager", c)
	//m := l.(*Manager)
	//uid := m.currentuserid()
	//cu := m.CurrentUser()
	//fmt.Printf("%+v %+v\n", uid, cu)
}

func testapp(name string, m *Manager) *flotilla.App {
	f := flotilla.New(name)
	m.Init(f)
	f.Configure(f.Configuration...)
	return f
}

func TestExtension(t *testing.T) {
	exists := false
	f := testapp("test-extension", basemanager())
	f.GET("/test", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		if _, ok := l.(*Manager); ok {
			exists = true
		}
		c.ServeData(200, []byte("success"))
	})
	PerformRequest(f, "GET", "/test")
	if !exists {
		t.Errorf("login extension does not exist")
	}
}

func TestLoginRequired(t *testing.T) {
	f := testapp("test-loginrequired", basemanager())
	f.GET("/loginrequired", LoginRequired(testhandler))
	r := PerformRequest(f, "GET", "/loginrequired")
	if r.Code != http.StatusUnauthorized {
		t.Errorf("Status code should be %v, was %d", http.StatusUnauthorized, r.Code)
	}
}

func TestRequireLogin(t *testing.T) {
	f := testapp("test-requirelogin", basemanager())
	f.UseAt(0, RequireLogin)
	f.GET("/requirelogin", testhandler)
	r := PerformRequest(f, "GET", "/requirelogin")
	if r.Code != http.StatusUnauthorized {
		t.Errorf("Status code should be %v, was %d", http.StatusUnauthorized, r.Code)
	}
}

func TestLogin(t *testing.T) {
	loggedin := false
	f := testapp("test-login", basemanager())
	f.POST("/login", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, true, true)
		if id := m.CurrentUser().GetId(); id == u.username {
			loggedin = true
		}
		c.ServeData(200, []byte("success"))
	})
	PerformRequest(f, "POST", "/login")
	if !loggedin {
		t.Errorf("login did not occur")
	}
}

func TestLogout(t *testing.T) {
	loggedin := false
	f := testapp("test-logout", basemanager())
	f.POST("/logout", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, false, false)
		m.LogoutUser()
		if id := m.CurrentUser().GetId(); id == u.username {
			loggedin = true
		}
		c.ServeData(200, []byte("success"))
	})
	PerformRequest(f, "POST", "/logout")
	if loggedin {
		t.Errorf("logout did not occur")
	}
}

/*
func TestRefresh(t *testing.T) {
	refreshed := false
	f := testapp("test-refresh", basemanager())
	f.GET("/refresh", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, false, true)
		refreshed = c.Session.Get("_fresh").(bool)
		c.ServeData(200, []byte("success"))
	})
	PerformRequest(f, "GET", "/refresh")
	if !refreshed {
		t.Errorf("refresh did not occur")
	}
}

//func TestRefreshRequired() {}

func TestRemembered(t *testing.T) {
	w := httptest.NewRecorder()
	f := testapp("test-remember", basemanager())
	f.GET("/remembered", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, true, true)
		c.ServeData(200, []byte("success"))
	})
	req, _ := http.NewRequest("GET", "/remembered", nil)
	f.ServeHTTP(w, req)
	for _, x := range w.Header()["Set-Cookie"] {
		fmt.Printf("%s\n", x)
	}
	fmt.Printf("1: %+v\n\n", reflect.TypeOf(w.Header()["Set-Cookie"]))
}
*/
<commit_msg>basic tests update<commit_after>package login

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/thrisp/flotilla"
)

type Tuser struct {
	username string
	active   bool
}

func (u *Tuser) IsAuthenticated() bool {
	return true
}

func (u *Tuser) IsActive() bool {
	return u.active
}

func (u *Tuser) IsAnonymous() bool {
	return false
}

func (u *Tuser) GetId() string {
	return u.username
}

var tusers map[string]*Tuser = map[string]*Tuser{"one": &Tuser{username: "User_One", active: true},
	"two": &Tuser{username: "User_Two"}}

func InMemoryUserLoader(s string) User {
	if u, ok := tusers[s]; ok {
		return u
	}
	return &AnonymousUser{}
}

func PerformRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func basemanager(c ...Configuration) *Manager {
	c = append(c, UserLoader(InMemoryUserLoader))
	l := New(c...)
	return l
}

func testhandler(c *flotilla.Ctx) {
	//passed := true
	//l, _ := c.Call("loginmanager", c)
	//m := l.(*Manager)
	//uid := m.currentuserid()
	//cu := m.CurrentUser()
	//fmt.Printf("%+v %+v\n", uid, cu)
}

func testapp(name string, m *Manager) *flotilla.App {
	f := flotilla.New(name, flotilla.DefaultEngine)
	m.Init(f)
	f.Configure(f.Configuration...)
	return f
}

func TestExtension(t *testing.T) {
	exists := false
	f := testapp("test-extension", basemanager())
	f.GET("/test", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		if _, ok := l.(*Manager); ok {
			exists = true
		}
		c.ServePlain(200, []byte("success"))
	})
	PerformRequest(f, "GET", "/test")
	if !exists {
		t.Errorf("login extension does not exist")
	}
}

func TestLoginRequired(t *testing.T) {
	f := testapp("test-loginrequired", basemanager())
	f.GET("/loginrequired", LoginRequired(testhandler))
	r := PerformRequest(f, "GET", "/loginrequired")
	if r.Code != http.StatusUnauthorized {
		t.Errorf("LoginRequired: status code should be %v, was %d", http.StatusUnauthorized, r.Code)
	}
}

func TestRequireLogin(t *testing.T) {
	f := testapp("test-requirelogin", basemanager())
	f.UseAt(0, RequireLogin)
	f.GET("/requirelogin", testhandler)
	r := PerformRequest(f, "GET", "/requirelogin")
	if r.Code != http.StatusUnauthorized {
		t.Errorf("Status code should be %v, was %d", http.StatusUnauthorized, r.Code)
	}
}

func TestLogin(t *testing.T) {
	loggedin := false
	f := testapp("test-login", basemanager())
	f.POST("/login", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, true, true)
		if id := m.CurrentUser().GetId(); id == u.username {
			loggedin = true
		}
		c.ServePlain(200, []byte("success"))
	})
	PerformRequest(f, "POST", "/login")
	if !loggedin {
		t.Errorf("login did not occur")
	}
}

func TestLogout(t *testing.T) {
	loggedin := false
	f := testapp("test-logout", basemanager())
	f.POST("/logout", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, false, false)
		m.LogoutUser()
		if id := m.CurrentUser().GetId(); id == u.username {
			loggedin = true
		}
		c.ServePlain(200, []byte("success"))
	})
	PerformRequest(f, "POST", "/logout")
	if loggedin {
		t.Errorf("logout did not occur")
	}
}

func TestRefresh(t *testing.T) {
	refreshed := false
	f := testapp("test-refresh", basemanager())
	f.GET("/refresh", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, false, true)
		refreshed = c.Session.Get("_fresh").(bool)
		c.ServePlain(200, []byte("success"))
	})
	PerformRequest(f, "GET", "/refresh")
	if !refreshed {
		t.Errorf("refresh did not occur")
	}
}

func TestRefreshRequired(t *testing.T) {
	f := testapp("test-refresh", basemanager())
	f.GET("/refreshrequired", RefreshRequired(func(c *flotilla.Ctx) {}))
	r := PerformRequest(f, "GET", "/refreshrequired")
	if r.Code != http.StatusForbidden {
		t.Errorf("RefreshRequired: status code should be %v, was %d", http.StatusForbidden, r.Code)
	}
}

func TestRemembered(t *testing.T) {
	f := testapp("test-remember", basemanager())
	f.GET("/remembered", func(c *flotilla.Ctx) {
		l, _ := c.Call("loginmanager", c)
		m := l.(*Manager)
		u := tusers["one"]
		m.LoginUser(u, true, true)
		c.ServePlain(200, []byte("success"))
	})
	w := PerformRequest(f, "GET", "/remembered")
	if w.HeaderMap["Set-Cookie"][0][:14] != "remember_token" {
		t.Errorf("not remembered")
	}
}
<|endoftext|>


<commit_before>package pipe

import (
	pipe "."
	"bytes"
	"fmt"
	"os"
	"regexp"
	"time"
)

func Example() {
	err := pipe.Run(
		pipe.Find(pipe.FILES, "."),
		pipe.Grep(`pipe.*\.go$`),
		pipe.WriteLines(os.Stdout),
	)
	fmt.Println("error:", err)
	// Output:
	// pipe.go
	// pipe_test.go
	// error: <nil>
}

func ExampleArg_ReportError() {
	counter := func(arg pipe.Arg) {
		re, err := regexp.Compile("[")
		if err != nil {
			arg.ReportError(err)
			for _ = range arg.In {
				// Disard input
			}
			return
		}
		n := 1
		for s := range arg.In {
			if re.MatchString(s) {
				n++
			}
		}
		arg.Out <- fmt.Sprint(n)
	}
	err := pipe.Run(
		pipe.Numbers(1, 100),
		counter,
		pipe.WriteLines(os.Stdout),
	)
	if err == nil {
		fmt.Println("did not catch error")
	}
	// Output:
}

func ExampleSequence() {
	pipe.ForEach(pipe.Sequence(
		pipe.Echo("1 of 3"),
		pipe.Echo("2 of 3"),
		pipe.Echo("3 of 3"),
	), func(s string) { fmt.Println(s) })
	// Output:
	// 1 of 3
	// 2 of 3
	// 3 of 3
}

func ExampleForEach() {
	err := pipe.ForEach(pipe.Numbers(1, 5), func(s string) {
		fmt.Print(s)
	})
	if err != nil {
		panic(err)
	}
	// Output:
	// 12345
}

func ExampleRun() {
	pipe.Run(
		pipe.Echo("line 1"),
		pipe.Echo("line 2"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// line 1
	// line 2
}

func ExampleEcho() {
	pipe.Run(
		pipe.Echo("hello", "world"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// hello
	// world
}

func ExampleNumbers() {
	pipe.Run(
		pipe.Numbers(2, 5),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 2
	// 3
	// 4
	// 5
}

func ExampleMap() {
	pipe.Run(
		pipe.Echo("hello", "there", "how", "are", "you?"),
		pipe.Map(func(s string) string {
			return fmt.Sprintf("%d %s", len(s), s)
		}),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 5 hello
	// 5 there
	// 3 how
	// 3 are
	// 4 you?
}

func ExampleIf() {
	pipe.Run(
		pipe.Numbers(1, 12),
		pipe.If(func(s string) bool { return len(s) > 1 }),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrep() {
	pipe.Run(
		pipe.Numbers(1, 12),
		pipe.Grep(".."),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrepNot() {
	pipe.Run(
		pipe.Numbers(1, 12),
		pipe.GrepNot("^.$"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleUniq() {
	pipe.Run(
		pipe.Echo("a", "b", "b", "c", "b"),
		pipe.Uniq(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// a
	// b
	// c
	// b
}

func ExampleUniqWithCount() {
	pipe.Run(
		pipe.Echo("a", "b", "b", "c"),
		pipe.UniqWithCount(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1 a
	// 2 b
	// 1 c
}

func ExampleParallelMap() {
	pipe.Run(
		pipe.Echo("hello", "there", "how", "are", "you?"),
		pipe.ParallelMap(4, func(s string) string {
			// Sleep some amount to ensure that ParalellMap
			// implementation handles out of order results.
			time.Sleep(10 * time.Duration(len(s)) * time.Millisecond)
			return fmt.Sprintf("%d %s", len(s), s)
		}),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 5 hello
	// 5 there
	// 3 how
	// 3 are
	// 4 you?
}

func ExampleSubstitute() {
	pipe.Run(
		pipe.Numbers(1, 5),
		pipe.Substitute("(3)", "$1$1"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 33
	// 4
	// 5
}

func ExampleNumeric() {
	pipe.Run(
		pipe.Echo(
			"a 100",
			"b 20",
			"c notanumber", // Will sort last since column 2 is not a number
			"d",            // Will sort earliest since column 2 is missing
		),
		pipe.Sort(pipe.Numeric(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// d
	// b 20
	// a 100
	// c notanumber
}

func ExampleTextual() {
	pipe.Run(
		pipe.Echo(
			"10 bananas",
			"20 apples",
			"30", // Will sort first since column 2 is missing
		),
		pipe.Sort(pipe.Textual(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 30
	// 20 apples
	// 10 bananas
}

func ExampleDescending() {
	pipe.Run(
		pipe.Echo(
			"100",
			"20",
			"50",
		),
		pipe.Sort(pipe.Descending(pipe.Numeric(1))),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 100
	// 50
	// 20
}

func ExampleSort() {
	pipe.Run(
		pipe.Echo("banana", "apple", "cheese", "apple"),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// apple
	// apple
	// banana
	// cheese
}
func ExampleSort_twoTextColumns() {
	pipe.Run(
		pipe.Echo(
			"2 green bananas",
			"3 red apples",
			"4 yellow bananas",
			"5 brown pears",
			"6 green apples",
		),
		pipe.Sort(pipe.Textual(2), pipe.Textual(3)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 5 brown pears
	// 6 green apples
	// 2 green bananas
	// 3 red apples
	// 4 yellow bananas
}

func ExampleSort_twoNumericColumns() {
	pipe.Run(
		pipe.Echo(
			"1970 12",
			"1970 6",
			"1950 6",
			"1980 9",
		),
		pipe.Sort(pipe.Numeric(1), pipe.Numeric(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1950 6
	// 1970 6
	// 1970 12
	// 1980 9
}

func ExampleSort_mixedColumns() {
	pipe.Run(
		pipe.Echo(
			"1970 march",
			"1970 feb",
			"1950 june",
			"1980 sep",
		),
		pipe.Sort(pipe.Numeric(1), pipe.Textual(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1950 june
	// 1970 feb
	// 1970 march
	// 1980 sep
}

func ExampleReverse() {
	pipe.Run(
		pipe.Echo("a", "b"),
		pipe.Reverse(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// b
	// a
}

func ExampleSample() {
	pipe.Run(
		pipe.Numbers(100, 200),
		pipe.Sample(4),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 122
	// 150
	// 154
	// 158
}

func ExampleFirst() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.First(3),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleLast() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.Last(2),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropFirst() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.DropFirst(8),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropLast() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.DropLast(3),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
	// 4
	// 5
	// 6
	// 7
}

func ExampleNumberLines() {
	pipe.Run(
		pipe.Echo("a", "b"),
		pipe.NumberLines(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	//     1 a
	//     2 b
}

func ExampleSlice() {
	pipe.Run(
		pipe.Echo("hello", "world."),
		pipe.Slice(2, 5),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// llo
	// rld
}

func ExampleSelect() {
	pipe.Run(
		pipe.Echo("hello world"),
		pipe.Select(2, 3, 0, 1),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// world hello world hello
}

func ExampleFind() {
	pipe.Run(
		pipe.Find(pipe.FILES, "."),
		pipe.Grep("pipe"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// pipe.go
	// pipe_test.go
}

func ExampleFind_dirs() {
	pipe.Run(
		pipe.Find(pipe.DIRS, "."),
		pipe.GrepNot("git"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// .
}

func ExampleFind_error() {
	err := pipe.Run(pipe.Find(pipe.ALL, "/no_such_dir"))
	if err == nil {
		fmt.Println("pipe.Find did not return expected error")
	}
	// Output:
}

func ExampleCat() {
	pipe.Run(
		pipe.Cat("pipe_test.go"),
		pipe.Grep("^func ExampleCat"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// func ExampleCat() {
}

func ExampleWriteLines() {
	pipe.Run(
		pipe.Numbers(1, 3),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleReadLines() {
	pipe.Run(
		pipe.ReadLines(bytes.NewBufferString("the\nquick\nbrown\nfox\n")),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// brown
	// fox
	// quick
	// the
}

func ExampleCommandOutput() {
	pipe.Run(
		pipe.CommandOutput("find", ".", "-type", "f", "-print"),
		pipe.Grep(`^\./pipe.*\.go$`),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)

	// Output:
	// ./pipe.go
	// ./pipe_test.go
}

func ExampleCommandOutput_error() {
	err := pipe.Run(pipe.CommandOutput("no_such_command"))
	if err == nil {
		fmt.Println("execution of missing command succeeded unexpectedly")
	}
	// Output:
}
<commit_msg>fix typo<commit_after>package pipe

import (
	pipe "."
	"bytes"
	"fmt"
	"os"
	"regexp"
	"time"
)

func Example() {
	err := pipe.Run(
		pipe.Find(pipe.FILES, "."),
		pipe.Grep(`pipe.*\.go$`),
		pipe.WriteLines(os.Stdout),
	)
	fmt.Println("error:", err)
	// Output:
	// pipe.go
	// pipe_test.go
	// error: <nil>
}

func ExampleArg_ReportError() {
	counter := func(arg pipe.Arg) {
		re, err := regexp.Compile("[")
		if err != nil {
			arg.ReportError(err)
			for _ = range arg.In {
				// Discard input
			}
			return
		}
		n := 1
		for s := range arg.In {
			if re.MatchString(s) {
				n++
			}
		}
		arg.Out <- fmt.Sprint(n)
	}
	err := pipe.Run(
		pipe.Numbers(1, 100),
		counter,
		pipe.WriteLines(os.Stdout),
	)
	if err == nil {
		fmt.Println("did not catch error")
	}
	// Output:
}

func ExampleSequence() {
	pipe.ForEach(pipe.Sequence(
		pipe.Echo("1 of 3"),
		pipe.Echo("2 of 3"),
		pipe.Echo("3 of 3"),
	), func(s string) { fmt.Println(s) })
	// Output:
	// 1 of 3
	// 2 of 3
	// 3 of 3
}

func ExampleForEach() {
	err := pipe.ForEach(pipe.Numbers(1, 5), func(s string) {
		fmt.Print(s)
	})
	if err != nil {
		panic(err)
	}
	// Output:
	// 12345
}

func ExampleRun() {
	pipe.Run(
		pipe.Echo("line 1"),
		pipe.Echo("line 2"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// line 1
	// line 2
}

func ExampleEcho() {
	pipe.Run(
		pipe.Echo("hello", "world"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// hello
	// world
}

func ExampleNumbers() {
	pipe.Run(
		pipe.Numbers(2, 5),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 2
	// 3
	// 4
	// 5
}

func ExampleMap() {
	pipe.Run(
		pipe.Echo("hello", "there", "how", "are", "you?"),
		pipe.Map(func(s string) string {
			return fmt.Sprintf("%d %s", len(s), s)
		}),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 5 hello
	// 5 there
	// 3 how
	// 3 are
	// 4 you?
}

func ExampleIf() {
	pipe.Run(
		pipe.Numbers(1, 12),
		pipe.If(func(s string) bool { return len(s) > 1 }),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrep() {
	pipe.Run(
		pipe.Numbers(1, 12),
		pipe.Grep(".."),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrepNot() {
	pipe.Run(
		pipe.Numbers(1, 12),
		pipe.GrepNot("^.$"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleUniq() {
	pipe.Run(
		pipe.Echo("a", "b", "b", "c", "b"),
		pipe.Uniq(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// a
	// b
	// c
	// b
}

func ExampleUniqWithCount() {
	pipe.Run(
		pipe.Echo("a", "b", "b", "c"),
		pipe.UniqWithCount(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1 a
	// 2 b
	// 1 c
}

func ExampleParallelMap() {
	pipe.Run(
		pipe.Echo("hello", "there", "how", "are", "you?"),
		pipe.ParallelMap(4, func(s string) string {
			// Sleep some amount to ensure that ParalellMap
			// implementation handles out of order results.
			time.Sleep(10 * time.Duration(len(s)) * time.Millisecond)
			return fmt.Sprintf("%d %s", len(s), s)
		}),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 5 hello
	// 5 there
	// 3 how
	// 3 are
	// 4 you?
}

func ExampleSubstitute() {
	pipe.Run(
		pipe.Numbers(1, 5),
		pipe.Substitute("(3)", "$1$1"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 33
	// 4
	// 5
}

func ExampleNumeric() {
	pipe.Run(
		pipe.Echo(
			"a 100",
			"b 20",
			"c notanumber", // Will sort last since column 2 is not a number
			"d",            // Will sort earliest since column 2 is missing
		),
		pipe.Sort(pipe.Numeric(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// d
	// b 20
	// a 100
	// c notanumber
}

func ExampleTextual() {
	pipe.Run(
		pipe.Echo(
			"10 bananas",
			"20 apples",
			"30", // Will sort first since column 2 is missing
		),
		pipe.Sort(pipe.Textual(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 30
	// 20 apples
	// 10 bananas
}

func ExampleDescending() {
	pipe.Run(
		pipe.Echo(
			"100",
			"20",
			"50",
		),
		pipe.Sort(pipe.Descending(pipe.Numeric(1))),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 100
	// 50
	// 20
}

func ExampleSort() {
	pipe.Run(
		pipe.Echo("banana", "apple", "cheese", "apple"),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// apple
	// apple
	// banana
	// cheese
}
func ExampleSort_twoTextColumns() {
	pipe.Run(
		pipe.Echo(
			"2 green bananas",
			"3 red apples",
			"4 yellow bananas",
			"5 brown pears",
			"6 green apples",
		),
		pipe.Sort(pipe.Textual(2), pipe.Textual(3)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 5 brown pears
	// 6 green apples
	// 2 green bananas
	// 3 red apples
	// 4 yellow bananas
}

func ExampleSort_twoNumericColumns() {
	pipe.Run(
		pipe.Echo(
			"1970 12",
			"1970 6",
			"1950 6",
			"1980 9",
		),
		pipe.Sort(pipe.Numeric(1), pipe.Numeric(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1950 6
	// 1970 6
	// 1970 12
	// 1980 9
}

func ExampleSort_mixedColumns() {
	pipe.Run(
		pipe.Echo(
			"1970 march",
			"1970 feb",
			"1950 june",
			"1980 sep",
		),
		pipe.Sort(pipe.Numeric(1), pipe.Textual(2)),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1950 june
	// 1970 feb
	// 1970 march
	// 1980 sep
}

func ExampleReverse() {
	pipe.Run(
		pipe.Echo("a", "b"),
		pipe.Reverse(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// b
	// a
}

func ExampleSample() {
	pipe.Run(
		pipe.Numbers(100, 200),
		pipe.Sample(4),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 122
	// 150
	// 154
	// 158
}

func ExampleFirst() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.First(3),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleLast() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.Last(2),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropFirst() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.DropFirst(8),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropLast() {
	pipe.Run(
		pipe.Numbers(1, 10),
		pipe.DropLast(3),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
	// 4
	// 5
	// 6
	// 7
}

func ExampleNumberLines() {
	pipe.Run(
		pipe.Echo("a", "b"),
		pipe.NumberLines(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	//     1 a
	//     2 b
}

func ExampleSlice() {
	pipe.Run(
		pipe.Echo("hello", "world."),
		pipe.Slice(2, 5),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// llo
	// rld
}

func ExampleSelect() {
	pipe.Run(
		pipe.Echo("hello world"),
		pipe.Select(2, 3, 0, 1),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// world hello world hello
}

func ExampleFind() {
	pipe.Run(
		pipe.Find(pipe.FILES, "."),
		pipe.Grep("pipe"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// pipe.go
	// pipe_test.go
}

func ExampleFind_dirs() {
	pipe.Run(
		pipe.Find(pipe.DIRS, "."),
		pipe.GrepNot("git"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// .
}

func ExampleFind_error() {
	err := pipe.Run(pipe.Find(pipe.ALL, "/no_such_dir"))
	if err == nil {
		fmt.Println("pipe.Find did not return expected error")
	}
	// Output:
}

func ExampleCat() {
	pipe.Run(
		pipe.Cat("pipe_test.go"),
		pipe.Grep("^func ExampleCat"),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// func ExampleCat() {
}

func ExampleWriteLines() {
	pipe.Run(
		pipe.Numbers(1, 3),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleReadLines() {
	pipe.Run(
		pipe.ReadLines(bytes.NewBufferString("the\nquick\nbrown\nfox\n")),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)
	// Output:
	// brown
	// fox
	// quick
	// the
}

func ExampleCommandOutput() {
	pipe.Run(
		pipe.CommandOutput("find", ".", "-type", "f", "-print"),
		pipe.Grep(`^\./pipe.*\.go$`),
		pipe.Sort(),
		pipe.WriteLines(os.Stdout),
	)

	// Output:
	// ./pipe.go
	// ./pipe_test.go
}

func ExampleCommandOutput_error() {
	err := pipe.Run(pipe.CommandOutput("no_such_command"))
	if err == nil {
		fmt.Println("execution of missing command succeeded unexpectedly")
	}
	// Output:
}
<|endoftext|>


<commit_before>package nio

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"testing"
)

func TestPipe(t *testing.T) {
	r, w := Pipe()

	go func() {
		for i := 0; i < 10; i++ {
			w.Write([]byte(fmt.Sprintf("%d", i)))
		}
		w.Close()
	}()

	data, err := ioutil.ReadAll(r)
	if err != nil {
		t.Error(err.Error())
	}
	if !bytes.Equal(data, []byte("0123456789")) {
		t.Error("Not equal!")
	}
}
<commit_msg>Added break into test if Write fails<commit_after>package nio

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"testing"
)

func TestPipe(t *testing.T) {
	r, w := Pipe()

	go func() {
		for i := 0; i < 10; i++ {
			if _, err := w.Write([]byte(fmt.Sprintf("%d", i))); err != nil {
				t.Error(err.Error())
				return
			}
		}
		w.Close()
	}()

	data, err := ioutil.ReadAll(r)
	if err != nil {
		t.Error(err.Error())
	}
	if !bytes.Equal(data, []byte("0123456789")) {
		t.Error("Not equal!")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Tigera, Inc. All rights reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ifacemonitor_test

import (
	"strings"
	"syscall"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/projectcalico/felix/go/felix/ifacemonitor"
	"github.com/projectcalico/felix/go/felix/set"
	"github.com/vishvananda/netlink"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type linkModel struct {
	index int
	state string
	addrs set.Set
}

type netlinkTest struct {
	linkUpdates chan netlink.LinkUpdate
	addrUpdates chan netlink.AddrUpdate

	nextIndex int
	links     map[string]linkModel
}

func (nl *netlinkTest) addLink(name string) {
	if nl.links == nil {
		nl.links = map[string]linkModel{}
		nl.nextIndex = 10
	}
	nl.links[name] = linkModel{
		index: nl.nextIndex,
		state: "down",
		addrs: set.New(),
	}
	nl.nextIndex++
	nl.signalLink(name)
}

func (nl *netlinkTest) changeLinkState(name string, state string) {
	link := nl.links[name]
	link.state = state
	nl.links[name] = link
	nl.signalLink(name)
}

func (nl *netlinkTest) delLink(name string) {
	delete(nl.links, name)
	nl.signalLink(name)
}

func (nl *netlinkTest) signalLink(name string) {
	// Values for a link that does not exist...
	index := 0
	var rawFlags uint32 = 0
	var msgType uint16 = syscall.RTM_DELLINK

	// If the link does exist, overwrite appropriately.
	link, prs := nl.links[name]
	if prs {
		msgType = syscall.RTM_NEWLINK
		index = link.index
		if link.state == "up" {
			rawFlags = syscall.IFF_RUNNING
		}
	}

	// Build the update.
	update := netlink.LinkUpdate{
		Header: syscall.NlMsghdr{
			Type: msgType,
		},
		Link: &netlink.Dummy{
			LinkAttrs: netlink.LinkAttrs{
				Name:     name,
				Index:    index,
				RawFlags: rawFlags,
			},
		},
	}

	// Send it.
	log.Info("Test code signaling a link update")
	nl.linkUpdates <- update
}

func (nl *netlinkTest) addAddr(name string, addr string) {
	link := nl.links[name]
	link.addrs.Add(addr)
	nl.links[name] = link
	nl.signalAddr(name, addr, true)
}

func (nl *netlinkTest) delAddr(name string, addr string) {
	link := nl.links[name]
	link.addrs.Discard(addr)
	nl.links[name] = link
	nl.signalAddr(name, addr, false)
}

func (nl *netlinkTest) signalAddr(name string, addr string, exists bool) {
	// Build the update.
	net, err := netlink.ParseIPNet(addr)
	if err != nil {
		panic("Address parsing failed")
	}
	update := netlink.AddrUpdate{
		LinkIndex:   nl.links[name].index,
		NewAddr:     exists,
		LinkAddress: *net,
	}

	// Send it.
	log.Info("Test code signaling an addr update")
	nl.addrUpdates <- update
}

func (nl *netlinkTest) Subscribe(
	linkUpdates chan netlink.LinkUpdate,
	addrUpdates chan netlink.AddrUpdate,
) error {
	nl.linkUpdates = linkUpdates
	nl.addrUpdates = addrUpdates
	return nil
}

func (nl *netlinkTest) LinkList() ([]netlink.Link, error) {
	links := []netlink.Link{}
	for name, link := range nl.links {
		var rawFlags uint32 = 0
		if link.state == "up" {
			rawFlags = syscall.IFF_RUNNING
		}
		links = append(links, &netlink.Dummy{
			LinkAttrs: netlink.LinkAttrs{
				Name:     name,
				Index:    link.index,
				RawFlags: rawFlags,
			},
		})
	}
	return links, nil
}

func (nl *netlinkTest) AddrList(link netlink.Link, family int) ([]netlink.Addr, error) {
	name := link.Attrs().Name
	model, prs := nl.links[name]
	addrs := []netlink.Addr{}
	if prs {
		model.addrs.Iter(func(item interface{}) error {
			addr := item.(string)
			net, err := netlink.ParseIPNet(addr)
			if err != nil {
				panic("Address parsing failed")
			}
			if strings.ContainsRune(addr, ':') {
				if family == netlink.FAMILY_V6 {
					addrs = append(addrs, netlink.Addr{
						IPNet: net,
					})
				}
			} else {
				if family == netlink.FAMILY_V4 {
					addrs = append(addrs, netlink.Addr{
						IPNet: net,
					})
				}
			}
			return nil
		})
	}
	return addrs, nil
}

var linkC = make(chan string, 1)

func linkStateCallback(ifaceName string, ifaceState ifacemonitor.State) {
	log.Info("linkStateCallback: ifaceName=", ifaceName)
	log.Info("linkStateCallback: ifaceState=", ifaceState)
	linkC <- ifaceName
}

func expectLinkStateCb(ifaceName string) {
	cbIface := <-linkC
	Expect(cbIface).To(Equal(ifaceName))
}

var addrC = make(chan string, 1)

func addrStateCallback(ifaceName string, addrs set.Set) {
	log.Info("addrStateCallback: ifaceName=", ifaceName)
	log.Info("addrStateCallback: addrs=", addrs)
	addrC <- ifaceName
}

func expectAddrStateCb(ifaceName string) {
	cbIface := <-addrC
	Expect(cbIface).To(Equal(ifaceName))
}

var _ = Describe("ifacemonitor", func() {
	It("New", func() {

		// Make an Interface Monitor that uses a test netlink
		// stub implementation and resync trigger channel -
		// both controlled by this code.
		nl := &netlinkTest{}
		resyncC := make(chan time.Time)
		im := ifacemonitor.NewWithStubs(nl, resyncC)

		// Register this test code's callbacks, which (a) log;
		// and (b) send to a 1-buffered channel, so that the
		// test code _must_ explicitly indicate when it
		// expects those callbacks to have occurred.
		im.Callback = linkStateCallback
		im.AddrCallback = addrStateCallback

		// Start the monitor running, and give it time for its
		// initial resync (which will be a no-op) before we
		// start adding link state.
		go im.MonitorInterfaces()
		time.Sleep(10 * time.Millisecond)

		// Add a link and an address.  No callbacks expected
		// because the link is not up yet.
		nl.addLink("eth0")
		nl.addAddr("eth0", "10.0.240.10/24")

		// Set the link up, and expect callbacks.
		nl.changeLinkState("eth0", "up")
		expectLinkStateCb("eth0")
		expectAddrStateCb("eth0")

		// Add an address.
		nl.addAddr("eth0", "172.19.34.1/27")
		expectAddrStateCb("eth0")

		// Delete that address.
		nl.delAddr("eth0", "172.19.34.1/27")
		expectAddrStateCb("eth0")

		// Add address again.
		nl.addAddr("eth0", "172.19.34.1/27")
		expectAddrStateCb("eth0")

		// Delete an address that wasn't actually there - no callback.
		nl.delAddr("eth0", "8.8.8.8/32")

		// Set link down.
		nl.changeLinkState("eth0", "down")
		expectAddrStateCb("eth0")
		expectLinkStateCb("eth0")

		// Set link up again.
		nl.changeLinkState("eth0", "up")
		expectLinkStateCb("eth0")
		expectAddrStateCb("eth0")

		// Trigger a resync, then immediately delete the link.
		// What happens is that the test code deletes its
		// state for eth0 before the monitor's resync() calls
		// LinkList, and so the monitor reports "Spotted
		// interface removal on resync" and makes link and
		// address callbacks accordingly.
		resyncC <- time.Time{}
		nl.delLink("eth0")
		expectLinkStateCb("eth0")
		expectAddrStateCb("eth0")

		// Trigger another resync.  Nothing is expected.  We
		// ensure that the resync processing completes, before
		// exiting from this test, by sending a further resync
		// trigger.  (This would block if the interface
		// monitor's main loop was not yet ready to read it.)
		resyncC <- time.Time{}
		resyncC <- time.Time{}
	})
})
<commit_msg>Address callback now expected when link is down<commit_after>// Copyright (c) 2017 Tigera, Inc. All rights reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ifacemonitor_test

import (
	"strings"
	"syscall"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/projectcalico/felix/go/felix/ifacemonitor"
	"github.com/projectcalico/felix/go/felix/set"
	"github.com/vishvananda/netlink"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type linkModel struct {
	index int
	state string
	addrs set.Set
}

type netlinkTest struct {
	linkUpdates chan netlink.LinkUpdate
	addrUpdates chan netlink.AddrUpdate

	nextIndex int
	links     map[string]linkModel
}

func (nl *netlinkTest) addLink(name string) {
	if nl.links == nil {
		nl.links = map[string]linkModel{}
		nl.nextIndex = 10
	}
	nl.links[name] = linkModel{
		index: nl.nextIndex,
		state: "down",
		addrs: set.New(),
	}
	nl.nextIndex++
	nl.signalLink(name)
}

func (nl *netlinkTest) changeLinkState(name string, state string) {
	link := nl.links[name]
	link.state = state
	nl.links[name] = link
	nl.signalLink(name)
}

func (nl *netlinkTest) delLink(name string) {
	delete(nl.links, name)
	nl.signalLink(name)
}

func (nl *netlinkTest) signalLink(name string) {
	// Values for a link that does not exist...
	index := 0
	var rawFlags uint32 = 0
	var msgType uint16 = syscall.RTM_DELLINK

	// If the link does exist, overwrite appropriately.
	link, prs := nl.links[name]
	if prs {
		msgType = syscall.RTM_NEWLINK
		index = link.index
		if link.state == "up" {
			rawFlags = syscall.IFF_RUNNING
		}
	}

	// Build the update.
	update := netlink.LinkUpdate{
		Header: syscall.NlMsghdr{
			Type: msgType,
		},
		Link: &netlink.Dummy{
			LinkAttrs: netlink.LinkAttrs{
				Name:     name,
				Index:    index,
				RawFlags: rawFlags,
			},
		},
	}

	// Send it.
	log.Info("Test code signaling a link update")
	nl.linkUpdates <- update
}

func (nl *netlinkTest) addAddr(name string, addr string) {
	link := nl.links[name]
	link.addrs.Add(addr)
	nl.links[name] = link
	nl.signalAddr(name, addr, true)
}

func (nl *netlinkTest) delAddr(name string, addr string) {
	link := nl.links[name]
	link.addrs.Discard(addr)
	nl.links[name] = link
	nl.signalAddr(name, addr, false)
}

func (nl *netlinkTest) signalAddr(name string, addr string, exists bool) {
	// Build the update.
	net, err := netlink.ParseIPNet(addr)
	if err != nil {
		panic("Address parsing failed")
	}
	update := netlink.AddrUpdate{
		LinkIndex:   nl.links[name].index,
		NewAddr:     exists,
		LinkAddress: *net,
	}

	// Send it.
	log.Info("Test code signaling an addr update")
	nl.addrUpdates <- update
}

func (nl *netlinkTest) Subscribe(
	linkUpdates chan netlink.LinkUpdate,
	addrUpdates chan netlink.AddrUpdate,
) error {
	nl.linkUpdates = linkUpdates
	nl.addrUpdates = addrUpdates
	return nil
}

func (nl *netlinkTest) LinkList() ([]netlink.Link, error) {
	links := []netlink.Link{}
	for name, link := range nl.links {
		var rawFlags uint32 = 0
		if link.state == "up" {
			rawFlags = syscall.IFF_RUNNING
		}
		links = append(links, &netlink.Dummy{
			LinkAttrs: netlink.LinkAttrs{
				Name:     name,
				Index:    link.index,
				RawFlags: rawFlags,
			},
		})
	}
	return links, nil
}

func (nl *netlinkTest) AddrList(link netlink.Link, family int) ([]netlink.Addr, error) {
	name := link.Attrs().Name
	model, prs := nl.links[name]
	addrs := []netlink.Addr{}
	if prs {
		model.addrs.Iter(func(item interface{}) error {
			addr := item.(string)
			net, err := netlink.ParseIPNet(addr)
			if err != nil {
				panic("Address parsing failed")
			}
			if strings.ContainsRune(addr, ':') {
				if family == netlink.FAMILY_V6 {
					addrs = append(addrs, netlink.Addr{
						IPNet: net,
					})
				}
			} else {
				if family == netlink.FAMILY_V4 {
					addrs = append(addrs, netlink.Addr{
						IPNet: net,
					})
				}
			}
			return nil
		})
	}
	return addrs, nil
}

var linkC = make(chan string, 1)

func linkStateCallback(ifaceName string, ifaceState ifacemonitor.State) {
	log.Info("linkStateCallback: ifaceName=", ifaceName)
	log.Info("linkStateCallback: ifaceState=", ifaceState)
	linkC <- ifaceName
}

func expectLinkStateCb(ifaceName string) {
	cbIface := <-linkC
	Expect(cbIface).To(Equal(ifaceName))
}

var addrC = make(chan string, 1)

func addrStateCallback(ifaceName string, addrs set.Set) {
	log.Info("addrStateCallback: ifaceName=", ifaceName)
	log.Info("addrStateCallback: addrs=", addrs)
	addrC <- ifaceName
}

func expectAddrStateCb(ifaceName string) {
	cbIface := <-addrC
	Expect(cbIface).To(Equal(ifaceName))
}

var _ = Describe("ifacemonitor", func() {
	It("New", func() {

		// Make an Interface Monitor that uses a test netlink
		// stub implementation and resync trigger channel -
		// both controlled by this code.
		nl := &netlinkTest{}
		resyncC := make(chan time.Time)
		im := ifacemonitor.NewWithStubs(nl, resyncC)

		// Register this test code's callbacks, which (a) log;
		// and (b) send to a 1-buffered channel, so that the
		// test code _must_ explicitly indicate when it
		// expects those callbacks to have occurred.
		im.Callback = linkStateCallback
		im.AddrCallback = addrStateCallback

		// Start the monitor running, and give it time for its
		// initial resync (which will be a no-op) before we
		// start adding link state.
		go im.MonitorInterfaces()
		time.Sleep(10 * time.Millisecond)

		// Add a link and an address.  No link callback
		// expected because the link is not up yet.  But we do
		// get an address callback because those are
		// independent of link state.
		nl.addLink("eth0")
		nl.addAddr("eth0", "10.0.240.10/24")
		expectAddrStateCb("eth0")

		// Set the link up, and expect callbacks.
		nl.changeLinkState("eth0", "up")
		expectLinkStateCb("eth0")
		expectAddrStateCb("eth0")

		// Add an address.
		nl.addAddr("eth0", "172.19.34.1/27")
		expectAddrStateCb("eth0")

		// Delete that address.
		nl.delAddr("eth0", "172.19.34.1/27")
		expectAddrStateCb("eth0")

		// Add address again.
		nl.addAddr("eth0", "172.19.34.1/27")
		expectAddrStateCb("eth0")

		// Delete an address that wasn't actually there - no callback.
		nl.delAddr("eth0", "8.8.8.8/32")

		// Set link down.
		nl.changeLinkState("eth0", "down")
		expectAddrStateCb("eth0")
		expectLinkStateCb("eth0")

		// Set link up again.
		nl.changeLinkState("eth0", "up")
		expectLinkStateCb("eth0")
		expectAddrStateCb("eth0")

		// Trigger a resync, then immediately delete the link.
		// What happens is that the test code deletes its
		// state for eth0 before the monitor's resync() calls
		// LinkList, and so the monitor reports "Spotted
		// interface removal on resync" and makes link and
		// address callbacks accordingly.
		resyncC <- time.Time{}
		nl.delLink("eth0")
		expectLinkStateCb("eth0")
		expectAddrStateCb("eth0")

		// Trigger another resync.  Nothing is expected.  We
		// ensure that the resync processing completes, before
		// exiting from this test, by sending a further resync
		// trigger.  (This would block if the interface
		// monitor's main loop was not yet ready to read it.)
		resyncC <- time.Time{}
		resyncC <- time.Time{}
	})
})
<|endoftext|>


<commit_before>package plugin

/*
usage: !wa Richard Stallman
*/

import (
	"encoding/xml"
	"fmt"
	"github.com/microamp/gerri/cmd"
	"github.com/microamp/gerri/data"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

func WAResult(config *data.Config, query string) (*data.WA, error) {
	var results *data.WA = &data.WA{}

	escaped_query := url.QueryEscape(strings.TrimSpace(query))
	resource := fmt.Sprintf("%s?input=%s&appid=%s", config.WaApi, escaped_query, config.WaKey)

	resp, err := http.Get(resource)
	if err != nil {
		return results, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return results, err
	}
	if err = xml.Unmarshal(body, results); err != nil {
		return results, err
	}
	return results, nil
}

func ReplyWA(pm data.Privmsg, config *data.Config) (string, error) {
	if len(pm.Message) >= 2 {
		query := strings.TrimSpace(strings.Join(pm.Message[1:], " "))
		results, err := WAResult(config, query)
		if err != nil {
			return "", err
		}

		var result_string string = ""
		for _, pod := range results.Pods {
			if pod.Title != "" && pod.Plaintext != "" {
				result_string += fmt.Sprintf("%s:{%s} ", pod.Title, pod.Plaintext)
			}
		}

		result_string = strings.Replace(string(result_string), "\n", " ", -1)
		words := strings.Split(result_string, " ")

		if len(words) > config.WaMaxWords {
			text := strings.Join(words[:config.WaMaxWords], " ")
			result_string = fmt.Sprintf("%s...", text)
		}

		return cmd.Privmsg(pm.Target, result_string), nil
	}
	return cmd.PrivmsgAction(pm.Target, "zzzzz..."), nil
}
<commit_msg>Bug fix in wa plugin.<commit_after>package plugin

/*
usage: !wa Richard Stallman
*/

import (
	"encoding/xml"
	"fmt"
	"github.com/microamp/gerri/cmd"
	"github.com/microamp/gerri/data"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

func WAResult(config *data.Config, query string) (*data.WA, error) {
	var results *data.WA = &data.WA{}

	escaped_query := url.QueryEscape(strings.TrimSpace(query))
	resource := fmt.Sprintf("%s?input=%s&appid=%s", config.WaApi, escaped_query, config.WaKey)

	resp, err := http.Get(resource)
	if err != nil {
		return results, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return results, err
	}
	if err = xml.Unmarshal(body, results); err != nil {
		return results, err
	}
	return results, nil
}

func ReplyWA(pm data.Privmsg, config *data.Config) (string, error) {
	if len(pm.Message) >= 2 {
		query := strings.TrimSpace(strings.Join(pm.Message[1:], " "))
		results, err := WAResult(config, query)
		if err != nil {
			return "", err
		}

		var result_string string = ""
		for _, pod := range results.Pods {
			if pod.Title != "" && pod.Plaintext != "" {
				result_string += fmt.Sprintf("%s:{%s} ", pod.Title, pod.Plaintext)
			}
		}

		result_string = strings.Replace(string(result_string), "\n", " ", -1)
		words := strings.Split(result_string, " ")

		if len(words) > config.WaMaxWords {
			text := strings.Join(words[:config.WaMaxWords], " ")
			result_string = fmt.Sprintf("%s...", text)
		}

		if result_string != "" {
			return cmd.Privmsg(pm.Target, result_string), nil
		}
	}
	return cmd.PrivmsgAction(pm.Target, "zzzzz..."), nil
}
<|endoftext|>


<commit_before>// Obdi - a REST interface and GUI for deploying software
// Copyright (C) 2014  Mark Clarkson
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"fmt"
	"github.com/mclarkson/obdi/external/jinzhu/gorm"
	"html/template"
	"net/http"
	"os"
	"path"
	"sync"
)

var VERSION string

type Api struct {
	db       *gorm.DB
	port     int64
	apimutex *sync.Mutex
	compile  *sync.Mutex
}

type ApiError struct {
	details string
}

// SetDB: Allows to set the gorm.DB
//
func (api *Api) SetDB(db *gorm.DB) {
	api.db = db
}

// Port: Return a port to connect to for RPC and increment it for the
// next connection
func (api *Api) Port() int64 {
	apimutex.Lock()
	portnum := api.port
	api.port += 1
	apimutex.Unlock()
	return portnum
}

func (api *Api) SetPort(portnum int64) {
	apimutex.Lock()
	api.port = portnum
	apimutex.Unlock()
}

func (api *Api) DecrementPort() {
	apimutex.Lock()
	//startport,_ := strconv.ParseInt(config.GoPluginPortStart,10,64)
	if api.port != config.GoPluginPortStart {
		api.port -= 1
	}
	apimutex.Unlock()
}

// TouchSession: Updates the UpdatedAt field
//
// Takes the GUID is a parameter.
//
func (api *Api) TouchSession(guid string) {
	session := Session{}
	mutex.Lock()
	api.db.Where("guid = ?", guid).First(&session)
	session.UpdatedAt = time.Now()
	api.db.Save(&session)
	mutex.Unlock()
}

// LogActivity: Write a message to the activity log
//
func (api *Api) LogActivity(sesId int64, message string) {
	activity := Activity{
		Session_id: sesId,
		Message:    message,
	}
	mutex.Lock()
	api.db.Save(&activity)
	mutex.Unlock()
}

func (e ApiError) Error() string {
	return fmt.Sprintf("%s", e.details)
}

func (api *Api) CheckLoginNoExpiry(login, guid string) (Session, error) {

	user := User{}
	session := Session{}

	// select * from users where login = login
	mutex.Lock()
	if err := api.db.Where("login = ?", login).First(&user).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Invalid credentials."}
	}
	// select * from sessions where user_id = user.userid
	if err := api.db.Model(&user).Related(&session).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Not logged in."}
	}
	mutex.Unlock()

	// Check GUID
	if session.Guid != guid {
		return session, ApiError{"Invalid GUID."}
	}

	return session, nil
}

func (api *Api) CheckLogin(login, guid string) (Session, error) {

	user := User{}
	session := Session{}

	// select * from users where login = login
	mutex.Lock()
	if err := api.db.Where("login = ?", login).First(&user).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Invalid credentials."}
	}
	mutex.Unlock()

	// select * from sessions where user_id = user.userid
	mutex.Lock()
	if err := api.db.Model(&user).Related(&session).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Not logged in."}
	}
	mutex.Unlock()

	// Check GUID
	if session.Guid != guid {
		return session, ApiError{"Invalid GUID."}
	}

	// Check session age
	delta := time.Now().Sub(session.UpdatedAt)
	if delta.Minutes() > float64(config.SessionTimeout) {
		return session, ApiError{"Session expired."}
	}

	return session, nil
}

/*
 * The only purpose of serveRunTemplate is to add items to
 * the <head> block. Specifically to add AngularJS controller
 * files to support plugins.
 */
func (api *Api) serveRunTemplate(w http.ResponseWriter, r *http.Request) {

	/* Refer to:
	   http://www.alexedwards.net/blog/serving-static-sites-with-go
	   and
	   http://www.alexedwards.net/blog/a-recap-of-request-handling
	*/

	defaultScripts := []string{}

	// Split the default scripts for admin or run(user)
	// It's alot of unused scripts otherwise.
	if match, _ := path.Match("/manager/admin", r.URL.Path); match == true {
		defaultScripts = []string{
			`js/controllers/login.js`,
			`js/controllers/admin.js`,
			`js/controllers/users.js`,
			`js/controllers/dcs.js`,
			`js/controllers/envs.js`,
			`js/controllers/dccaps.js`,
			`js/controllers/envcaps.js`,
			`js/controllers/scripts.js`,
			`js/controllers/plugins.js`,
		}
	} else {
		// It's /manager/run
		defaultScripts = []string{
			`js/controllers/login.js`,
			`js/controllers/run.js`,
			`js/controllers/sidebar.js`,
		}
	}

	type IndexPageVars struct {
		Items   []string
		Version string
	}

	fp := path.Join(config.StaticContent + "/templates/main-index.html")

	logit(fp)
	// Return a 404 if the template doesn't exist
	info, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}
	}

	// Return a 404 if the request is for a directory
	if info.IsDir() {
		http.NotFound(w, r)
		return
	}

	// AngularJS uses {{ }} so we'll use [(
	// The following line creates an unnamed template and sets the
	// Delims for it. Parse files creates a further template named
	// as the file name - this name is used to execute the template
	// and it inherits the Delims. There's no other way to set the
	// Delims!
	tmpl, err := template.New("").Delims(`[(`, `)]`).ParseFiles(fp)
	if err != nil {
		// Log the detailed error
		logit(err.Error())
		// Return a generic "Internal Server Error" message
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// Search Files table for controllers that need adding to the
	// HEAD section
	files := []File{}
	mutex.Lock()
	api.db.Order("name").Find(&files, `url != "" and type == 1`)
	mutex.Unlock()

	//fmt.Printf( "%#v\n", files )
	scripts := []string{}
	for i := range files {
		scripts = append(scripts, "plugins/"+files[i].Url)
	}
	defaultScripts = append(defaultScripts, scripts...)

	data := &IndexPageVars{defaultScripts, VERSION}
	if err := tmpl.ExecuteTemplate(w, path.Base(fp), data); err != nil {
		logit(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

func NewApi(db *Database) Api {
	api := Api{}
	api.SetDB(&db.dB)
	//startport,_ := strconv.ParseInt(config.GoPluginPortStart,10,64)
	api.SetPort(config.GoPluginPortStart)
	api.compile = &sync.Mutex{}

	return api
}
<commit_msg>Fixed compile error<commit_after>// Obdi - a REST interface and GUI for deploying software
// Copyright (C) 2014  Mark Clarkson
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"fmt"
	"github.com/mclarkson/obdi/external/jinzhu/gorm"
	"html/template"
	"net/http"
	"os"
	"path"
	"sync"
	"time"
)

var VERSION string

type Api struct {
	db       *gorm.DB
	port     int64
	apimutex *sync.Mutex
	compile  *sync.Mutex
}

type ApiError struct {
	details string
}

// SetDB: Allows to set the gorm.DB
//
func (api *Api) SetDB(db *gorm.DB) {
	api.db = db
}

// Port: Return a port to connect to for RPC and increment it for the
// next connection
func (api *Api) Port() int64 {
	apimutex.Lock()
	portnum := api.port
	api.port += 1
	apimutex.Unlock()
	return portnum
}

func (api *Api) SetPort(portnum int64) {
	apimutex.Lock()
	api.port = portnum
	apimutex.Unlock()
}

func (api *Api) DecrementPort() {
	apimutex.Lock()
	//startport,_ := strconv.ParseInt(config.GoPluginPortStart,10,64)
	if api.port != config.GoPluginPortStart {
		api.port -= 1
	}
	apimutex.Unlock()
}

// TouchSession: Updates the UpdatedAt field
//
// Takes the GUID is a parameter.
//
func (api *Api) TouchSession(guid string) {
	session := Session{}
	mutex.Lock()
	api.db.Where("guid = ?", guid).First(&session)
	session.UpdatedAt = time.Now()
	api.db.Save(&session)
	mutex.Unlock()
}

// LogActivity: Write a message to the activity log
//
func (api *Api) LogActivity(sesId int64, message string) {
	activity := Activity{
		Session_id: sesId,
		Message:    message,
	}
	mutex.Lock()
	api.db.Save(&activity)
	mutex.Unlock()
}

func (e ApiError) Error() string {
	return fmt.Sprintf("%s", e.details)
}

func (api *Api) CheckLoginNoExpiry(login, guid string) (Session, error) {

	user := User{}
	session := Session{}

	// select * from users where login = login
	mutex.Lock()
	if err := api.db.Where("login = ?", login).First(&user).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Invalid credentials."}
	}
	// select * from sessions where user_id = user.userid
	if err := api.db.Model(&user).Related(&session).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Not logged in."}
	}
	mutex.Unlock()

	// Check GUID
	if session.Guid != guid {
		return session, ApiError{"Invalid GUID."}
	}

	return session, nil
}

func (api *Api) CheckLogin(login, guid string) (Session, error) {

	user := User{}
	session := Session{}

	// select * from users where login = login
	mutex.Lock()
	if err := api.db.Where("login = ?", login).First(&user).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Invalid credentials."}
	}
	mutex.Unlock()

	// select * from sessions where user_id = user.userid
	mutex.Lock()
	if err := api.db.Model(&user).Related(&session).Error; err != nil {
		mutex.Unlock()
		return session, ApiError{"Not logged in."}
	}
	mutex.Unlock()

	// Check GUID
	if session.Guid != guid {
		return session, ApiError{"Invalid GUID."}
	}

	// Check session age
	delta := time.Now().Sub(session.UpdatedAt)
	if delta.Minutes() > float64(config.SessionTimeout) {
		return session, ApiError{"Session expired."}
	}

	return session, nil
}

/*
 * The only purpose of serveRunTemplate is to add items to
 * the <head> block. Specifically to add AngularJS controller
 * files to support plugins.
 */
func (api *Api) serveRunTemplate(w http.ResponseWriter, r *http.Request) {

	/* Refer to:
	   http://www.alexedwards.net/blog/serving-static-sites-with-go
	   and
	   http://www.alexedwards.net/blog/a-recap-of-request-handling
	*/

	defaultScripts := []string{}

	// Split the default scripts for admin or run(user)
	// It's alot of unused scripts otherwise.
	if match, _ := path.Match("/manager/admin", r.URL.Path); match == true {
		defaultScripts = []string{
			`js/controllers/login.js`,
			`js/controllers/admin.js`,
			`js/controllers/users.js`,
			`js/controllers/dcs.js`,
			`js/controllers/envs.js`,
			`js/controllers/dccaps.js`,
			`js/controllers/envcaps.js`,
			`js/controllers/scripts.js`,
			`js/controllers/plugins.js`,
		}
	} else {
		// It's /manager/run
		defaultScripts = []string{
			`js/controllers/login.js`,
			`js/controllers/run.js`,
			`js/controllers/sidebar.js`,
		}
	}

	type IndexPageVars struct {
		Items   []string
		Version string
	}

	fp := path.Join(config.StaticContent + "/templates/main-index.html")

	logit(fp)
	// Return a 404 if the template doesn't exist
	info, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}
	}

	// Return a 404 if the request is for a directory
	if info.IsDir() {
		http.NotFound(w, r)
		return
	}

	// AngularJS uses {{ }} so we'll use [(
	// The following line creates an unnamed template and sets the
	// Delims for it. Parse files creates a further template named
	// as the file name - this name is used to execute the template
	// and it inherits the Delims. There's no other way to set the
	// Delims!
	tmpl, err := template.New("").Delims(`[(`, `)]`).ParseFiles(fp)
	if err != nil {
		// Log the detailed error
		logit(err.Error())
		// Return a generic "Internal Server Error" message
		http.Error(w, http.StatusText(500), 500)
		return
	}

	// Search Files table for controllers that need adding to the
	// HEAD section
	files := []File{}
	mutex.Lock()
	api.db.Order("name").Find(&files, `url != "" and type == 1`)
	mutex.Unlock()

	//fmt.Printf( "%#v\n", files )
	scripts := []string{}
	for i := range files {
		scripts = append(scripts, "plugins/"+files[i].Url)
	}
	defaultScripts = append(defaultScripts, scripts...)

	data := &IndexPageVars{defaultScripts, VERSION}
	if err := tmpl.ExecuteTemplate(w, path.Base(fp), data); err != nil {
		logit(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

func NewApi(db *Database) Api {
	api := Api{}
	api.SetDB(&db.dB)
	//startport,_ := strconv.ParseInt(config.GoPluginPortStart,10,64)
	api.SetPort(config.GoPluginPortStart)
	api.compile = &sync.Mutex{}

	return api
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package docker contains simple utilities for pulling a docker image, starting
// a container, and stoping a container. It assumes that docker is installed. If
// it is not, it will simply throw an error.
package docker

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	glog "third_party/go-glog"
)

const (
	shipshapeWork = "/shipshape-workspace"
	shipshapeLogs = "/shipshape-output"
)

// TODO(ciera): add some checking to ensure docker is installed.
// TODO(ciera): Consider making these all use channels.
type CommandResult struct {
	Stdout string
	Stderr string
	Err    error
}

func trimResult(stdout, stderr *bytes.Buffer, err error) CommandResult {
	return CommandResult{strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err}
}

// FullImageName creates a full image name from a repository URI, an image name, and a tag.
func FullImageName(repo, image, tag string) string {
	fullImage := repo
	if fullImage != "" && !strings.HasSuffix(fullImage, "/") {
		fullImage += "/"
	}
	fullImage += image

	if tag != "" {
		fullImage += ":" + tag
	}
	return fullImage
}

// Pull makes a command line call to docker to pull the specified container.
// docker pull repository/name:tag.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func Pull(image string) CommandResult {
	cmd := exec.Command("docker", "pull", image)
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

func setupArgs(container string, portMap map[int]int, volumeMap map[string]string, linkContainers []string, environment map[string]string) []string {
	var linkArgs []string
	for _, container := range linkContainers {
		linkArgs = append(linkArgs, fmt.Sprintf("--link=%s:%s", container, container))
	}

	var environmentVars []string
	for ev, val := range environment {
		environmentVars = append(environmentVars, "-e="+strconv.Quote(ev+"="+val))
	}

	var volumeList []string
	for hostVolume, containerVolume := range volumeMap {
		volumeList = append(volumeList, fmt.Sprintf("-v=%s:%s", hostVolume, containerVolume))
	}

	var exposePorts []string
	for hostPort, containerPort := range portMap {
		exposePorts = append(exposePorts, fmt.Sprintf("-p=127.0.0.1:%d:%d", hostPort, containerPort))
	}

	args := exposePorts
	args = append(args, linkArgs...)
	args = append(args, volumeList...)
	args = append(args, environmentVars...)
	args = append(args, fmt.Sprintf("--name=%s", container))
	return args
}

// RunAnalyzer runs the analyzer image with container analyzerContainer. It runs it at port (mapped
// to internal port 10005), binds the volumes for the workspacePath and logsPath, and gives the privileged
// if dind (docker-in-docker) is true.
func RunAnalyzer(image, analyzerContainer, workspacePath, logsPath string, port int, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(analyzerContainer) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		workspacePath: shipshapeWork,
		logsPath:      shipshapeLogs,
	}
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(analyzerContainer, map[int]int{port: 10005}, volumeMap, nil, nil)...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunService runs the shipshape service at image, as the container named container. It binds the
// shipshape workspace and logs appropriately. It starts with the third-party analyzers already
// running at analyzerContainers. The service is started with the privileged flag if dind (docker-in-docker)
// is true.
func RunService(image, container, workspacePath, logsPath string, analyzerContainers []string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{workspacePath: shipshapeWork, logsPath: shipshapeLogs}

	var locations []string
	for _, container := range analyzerContainers {
		locations = append(locations, fmt.Sprintf(`$%s_PORT_10005_TCP_ADDR:$%s_PORT_10005_TCP_PORT`, strings.ToUpper(container), strings.ToUpper(container)))
	}
	locations = append(locations, "localhost:10005", "localhost:10006", "localhost:10008")

	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, map[int]int{10007: 10007}, volumeMap, analyzerContainers, map[string]string{"START_SERVICE": "true", "ANALYZERS": strings.Join(locations, ",")})...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunStreams runs the specified shipshape image in streams mode, as the container named container.
// It binds the shipshape workspace and logs appropriately. It starts with the third-party analyzers
// already running at analyzerContainers. It uses input as the stdin and gives the privileged flag id
// dind (docker-in-docker) is true.
func RunStreams(image, container, workspacePath, logsPath string, analyzerContainers []string, input []byte, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{workspacePath: shipshapeWork, logsPath: shipshapeLogs}

	var locations []string
	for _, container := range analyzerContainers {
		locations = append(locations, fmt.Sprintf(`$%s_PORT_10005_TCP_ADDR:$%s_PORT_10005_TCP_PORT`, strings.ToUpper(container), strings.ToUpper(container)))
	}
	locations = append(locations, "localhost:10005", "localhost:10006")

	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, map[int]int{10007: 10007}, volumeMap, analyzerContainers, map[string]string{"ANALYZERS": strings.Join(locations, ",")})...)
	args = append(args, "-i", "-a", "stdin", "-a", "stderr", "-a", "stdout", image)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	cmd.Stdin = bytes.NewBuffer(input)
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunKythe runs the specified kythe docker image at the named container. It uses the
// source root and extractor specified, and gives the privileged flag if dind (docker-in-docker) is true.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func RunKythe(image, container, sourcePath, extractor string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		filepath.Join(sourcePath, "compilations"): "/compilations",
		sourcePath: "/repo",
	}
	home := os.Getenv("HOME")
	if len(home) > 0 {
		volumeMap[filepath.Join(home, ".m2")] = "/root/.m2"
	}

	// TODO(ciera): Can we exclude files in the .shipshape ignore path?
	// TODO(ciera/emso): Can we use the same command for blaze extraction?
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, nil, volumeMap, nil, nil)...)
	args = append(args, "-i", "-a", "stdin", "-a", "stderr", "-a", "stdout", image)
	args = append(args, "--extract", extractor)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// Stop stops a running container.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
// If requested, also remove the container.
func Stop(container string, waitTime time.Duration, remove bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if container == "" {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	cmd := exec.Command("docker", "stop", fmt.Sprintf("-t=%d", int(waitTime.Seconds())), container)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()

	if err == nil && remove {
		cmd := exec.Command("docker", "rm", container)
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		err = cmd.Run()
	}

	return CommandResult{stdout.String(), stderr.String(), err}
}

// OutOfDate returns true if the image specified
// has not been pulled recently.
func OutOfDate(image string) bool {
	// TODO(ciera): Rather than always return true,
	// check a file that contains the last time this
	// image was updated, and only return true if it
	// is at least N days old.
	return true
}

// ImageMatches returns whether the container is running
// the current version of image.
func ImageMatches(image, container string) bool {
	imageHash, err := inspect(image, "{{.Id}}")
	if err != nil {
		return false
	}
	containerHash, err := inspect(container, "{{.Image}}")
	if err != nil {
		return false
	}
	return bytes.Equal(imageHash, containerHash)
}

// MappedVolume returns whether path is already mapped into the workspace
// of the shipshape service running at container. If it is, it returns the relative path
// of path within the mapped volume.
func MappedVolume(path, container string) (bool, string) {
	// Why this big ugly mess you ask? Because we can't us a go template to index
	// like this: .Volumes./shipshape-workspace because go templates only allow
	// alphanumeric identifiers. So instead, we do this.
	v, err := inspect(container, `{{range $k, $v := .Volumes}} {{if eq $k "/shipshape-workspace"}} {{$v}} {{end}} {{end}}`)
	if err != nil {
		return false, ""
	}
	volume := strings.TrimSpace(string(v))
	// Handle the equal case
	if path == volume {
		return true, ""
	}
	// Handle the subdirectory case by adding a trailing '/'
	// Want to rule out the case: volume='/a/b2' and path='/a/b'
	path += "/"
	return strings.HasPrefix(volume, path), strings.TrimPrefix(path, volume)
}

// ContainsLinks returns whether the given container has links to the given
// list of containers.
func ContainsLinks(container string, linkedContainers []string) bool {
	l, err := inspect(container, `{{.HostConfig.Links}}`)
	if err != nil {
		return false
	}
	links := strings.TrimSpace(string(l))
	for _, linkedContainer := range linkedContainers {
		if !strings.Contains(links, linkedContainer) {
			return false
		}
	}
	return true
}

// inspect runs docker inspect on name, which must be either an image or a container.
// If non-empty, it uses the specified format string.
// Returns the combined stdout/stderr from running docker inspect
func inspect(name, format string) ([]byte, error) {
	var formatter string
	if len(format) != 0 {
		formatter = fmt.Sprintf("--format='%s'", format)
	}
	cmd := exec.Command("docker", "inspect", formatter, name)
	return cmd.CombinedOutput()
}
<commit_msg>Updates comment<commit_after>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package docker contains simple utilities for pulling a docker image, starting
// a container, and stoping a container. It assumes that docker is installed. If
// it is not, it will simply throw an error.
package docker

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	glog "third_party/go-glog"
)

const (
	shipshapeWork = "/shipshape-workspace"
	shipshapeLogs = "/shipshape-output"
)

// TODO(ciera): add some checking to ensure docker is installed.
// TODO(ciera): Consider making these all use channels.
type CommandResult struct {
	Stdout string
	Stderr string
	Err    error
}

func trimResult(stdout, stderr *bytes.Buffer, err error) CommandResult {
	return CommandResult{strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err}
}

// FullImageName creates a full image name from a repository URI, an image name, and a tag.
func FullImageName(repo, image, tag string) string {
	fullImage := repo
	if fullImage != "" && !strings.HasSuffix(fullImage, "/") {
		fullImage += "/"
	}
	fullImage += image

	if tag != "" {
		fullImage += ":" + tag
	}
	return fullImage
}

// Pull makes a command line call to docker to pull the specified container.
// docker pull repository/name:tag.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func Pull(image string) CommandResult {
	cmd := exec.Command("docker", "pull", image)
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

func setupArgs(container string, portMap map[int]int, volumeMap map[string]string, linkContainers []string, environment map[string]string) []string {
	var linkArgs []string
	for _, container := range linkContainers {
		linkArgs = append(linkArgs, fmt.Sprintf("--link=%s:%s", container, container))
	}

	var environmentVars []string
	for ev, val := range environment {
		environmentVars = append(environmentVars, "-e="+strconv.Quote(ev+"="+val))
	}

	var volumeList []string
	for hostVolume, containerVolume := range volumeMap {
		volumeList = append(volumeList, fmt.Sprintf("-v=%s:%s", hostVolume, containerVolume))
	}

	var exposePorts []string
	for hostPort, containerPort := range portMap {
		exposePorts = append(exposePorts, fmt.Sprintf("-p=127.0.0.1:%d:%d", hostPort, containerPort))
	}

	args := exposePorts
	args = append(args, linkArgs...)
	args = append(args, volumeList...)
	args = append(args, environmentVars...)
	args = append(args, fmt.Sprintf("--name=%s", container))
	return args
}

// RunAnalyzer runs the analyzer image with container analyzerContainer. It runs it at port (mapped
// to internal port 10005), binds the volumes for the workspacePath and logsPath, and gives the privileged
// if dind (docker-in-docker) is true.
func RunAnalyzer(image, analyzerContainer, workspacePath, logsPath string, port int, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(analyzerContainer) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		workspacePath: shipshapeWork,
		logsPath:      shipshapeLogs,
	}
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(analyzerContainer, map[int]int{port: 10005}, volumeMap, nil, nil)...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunService runs the shipshape service at image, as the container named container. It binds the
// shipshape workspace and logs appropriately. It starts with the third-party analyzers already
// running at analyzerContainers. The service is started with the privileged flag if dind (docker-in-docker)
// is true.
func RunService(image, container, workspacePath, logsPath string, analyzerContainers []string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{workspacePath: shipshapeWork, logsPath: shipshapeLogs}

	var locations []string
	for _, container := range analyzerContainers {
		locations = append(locations, fmt.Sprintf(`$%s_PORT_10005_TCP_ADDR:$%s_PORT_10005_TCP_PORT`, strings.ToUpper(container), strings.ToUpper(container)))
	}
	locations = append(locations, "localhost:10005", "localhost:10006", "localhost:10008")

	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, map[int]int{10007: 10007}, volumeMap, analyzerContainers, map[string]string{"START_SERVICE": "true", "ANALYZERS": strings.Join(locations, ",")})...)
	args = append(args, "-d", image)

	glog.Infof("Running 'docker %v'\n", args)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunStreams runs the specified shipshape image in streams mode, as the container named container.
// It binds the shipshape workspace and logs appropriately. It starts with the third-party analyzers
// already running at analyzerContainers. It uses input as the stdin and gives the privileged flag id
// dind (docker-in-docker) is true.
func RunStreams(image, container, workspacePath, logsPath string, analyzerContainers []string, input []byte, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{workspacePath: shipshapeWork, logsPath: shipshapeLogs}

	var locations []string
	for _, container := range analyzerContainers {
		locations = append(locations, fmt.Sprintf(`$%s_PORT_10005_TCP_ADDR:$%s_PORT_10005_TCP_PORT`, strings.ToUpper(container), strings.ToUpper(container)))
	}
	locations = append(locations, "localhost:10005", "localhost:10006")

	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, map[int]int{10007: 10007}, volumeMap, analyzerContainers, map[string]string{"ANALYZERS": strings.Join(locations, ",")})...)
	args = append(args, "-i", "-a", "stdin", "-a", "stderr", "-a", "stdout", image)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	cmd.Stdin = bytes.NewBuffer(input)
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// RunKythe runs the specified kythe docker image at the named container. It uses the
// source root and extractor specified, and gives the privileged flag if dind (docker-in-docker) is true.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
func RunKythe(image, container, sourcePath, extractor string, dind bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if len(container) == 0 {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	volumeMap := map[string]string{
		filepath.Join(sourcePath, "compilations"): "/compilations",
		sourcePath: "/repo",
	}
	home := os.Getenv("HOME")
	if len(home) > 0 {
		volumeMap[filepath.Join(home, ".m2")] = "/root/.m2"
	}

	// TODO(ciera): Can we exclude files in the .shipshape ignore path?
	// TODO(ciera/emso): Can we use the same command for blaze extraction?
	args := []string{"run"}
	if dind {
		args = append(args, "--privileged")
	}
	args = append(args, setupArgs(container, nil, volumeMap, nil, nil)...)
	args = append(args, "-i", "-a", "stdin", "-a", "stderr", "-a", "stdout", image)
	args = append(args, "--extract", extractor)

	cmd := exec.Command("docker", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()
	return CommandResult{stdout.String(), stderr.String(), err}
}

// Stop stops a running container.
// It returns stdout, stderr, and any errors from running.
// This is a blocking call, and should be wrapped in a go routine for asynchonous use.
// If requested, also remove the container.
func Stop(container string, waitTime time.Duration, remove bool) CommandResult {
	stdout := bytes.NewBuffer(nil)
	stderr := bytes.NewBuffer(nil)
	if container == "" {
		return CommandResult{"", "", errors.New("need to provide a name for the container")}
	}

	cmd := exec.Command("docker", "stop", fmt.Sprintf("-t=%d", int(waitTime.Seconds())), container)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	err := cmd.Run()

	if err == nil && remove {
		cmd := exec.Command("docker", "rm", container)
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		err = cmd.Run()
	}

	return CommandResult{stdout.String(), stderr.String(), err}
}

// OutOfDate returns true if the image specified
// has not been pulled recently.
func OutOfDate(image string) bool {
	// TODO(ciera): Rather than always return true,
	// check a file that contains the last time this
	// image was updated, and only return true if it
	// is at least N days old.
	return true
}

// ImageMatches returns whether the container is running
// the current version of image.
func ImageMatches(image, container string) bool {
	imageHash, err := inspect(image, "{{.Id}}")
	if err != nil {
		return false
	}
	containerHash, err := inspect(container, "{{.Image}}")
	if err != nil {
		return false
	}
	return bytes.Equal(imageHash, containerHash)
}

// MappedVolume returns whether path is already mapped into the workspace
// of the shipshape service running at container. If it is, it returns the relative path
// of path within the mapped volume.
func MappedVolume(path, container string) (bool, string) {
	// Why this big ugly mess you ask? Because we can't use a go template to index
	// like this: .Volumes./shipshape-workspace because go templates only allow
	// alphanumeric identifiers. So instead, we do this.
	v, err := inspect(container, `{{range $k, $v := .Volumes}} {{if eq $k "/shipshape-workspace"}} {{$v}} {{end}} {{end}}`)
	if err != nil {
		return false, ""
	}
	volume := strings.TrimSpace(string(v))
	// Handle the equal case
	if path == volume {
		return true, ""
	}
	// Handle the subdirectory case by adding a trailing '/'
	// Want to rule out the case: volume='/a/b2' and path='/a/b'
	path += "/"
	return strings.HasPrefix(volume, path), strings.TrimPrefix(path, volume)
}

// ContainsLinks returns whether the given container has links to the given
// list of containers.
func ContainsLinks(container string, linkedContainers []string) bool {
	l, err := inspect(container, `{{.HostConfig.Links}}`)
	if err != nil {
		return false
	}
	links := strings.TrimSpace(string(l))
	for _, linkedContainer := range linkedContainers {
		if !strings.Contains(links, linkedContainer) {
			return false
		}
	}
	return true
}

// inspect runs docker inspect on name, which must be either an image or a container.
// If non-empty, it uses the specified format string.
// Returns the combined stdout/stderr from running docker inspect
func inspect(name, format string) ([]byte, error) {
	var formatter string
	if len(format) != 0 {
		formatter = fmt.Sprintf("--format='%s'", format)
	}
	cmd := exec.Command("docker", "inspect", formatter, name)
	return cmd.CombinedOutput()
}
<|endoftext|>


<commit_before>package types

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/NebulousLabs/Sia/crypto"
)

// unlockhash.go contains the unlockhash alias along with usability methods
// such as String and an implementation of sort.Interface.

const (
	// UnlockHashChecksumSize is the size of the checksum used to verify
	// human-readable addresses. It is not a crypytographically secure
	// checksum, it's merely intended to prevent typos. 6 is chosen because it
	// brings the total size of the address to 38 bytes, leaving 2 bytes for
	// potential version additions in the future.
	UnlockHashChecksumSize = 6
)

// An UnlockHash is a specially constructed hash of the UnlockConditions type.
// "Locked" values can be unlocked by providing the UnlockConditions that hash
// to a given UnlockHash. See SpendConditions.UnlockHash for details on how the
// UnlockHash is constructed.
type UnlockHash crypto.Hash

type UnlockHashSlice []UnlockHash

// MarshalJSON is implemented on the unlock hash to always produce a hex string
// upon marshalling.
func (uh UnlockHash) MarshalJSON() ([]byte, error) {
	return json.Marshal(uh.String())
}

// UnmarshalJSON is implemented on the unlock hash to recover an unlock hash
// that has been encoded to a hex string.
func (uh *UnlockHash) UnmarshalJSON(b []byte) error {
	// Check the length of b.
	if len(b) != crypto.HashSize*2+UnlockHashChecksumSize*2+2 && len(b) != crypto.HashSize*2+2 {
		return ErrUnlockHashWrongLen
	}
	return uh.LoadString(string(b[1 : len(b)-1]))
}

// String returns the hex representation of the unlock hash as a string - this
// includes a checksum.
func (uh UnlockHash) String() string {
	uhChecksum := crypto.HashObject(uh)
	return fmt.Sprintf("%x%x", uh[:], uhChecksum[:UnlockHashChecksumSize])
}

// LoadString loads a hex representation (including checksum) of an unlock hash
// into an unlock hash object. An error is returned if the string is invalid or
// fails the checksum.
func (uh *UnlockHash) LoadString(strUH string) error {
	// Check the length of strUH.
	if len(strUH) != crypto.HashSize*2+UnlockHashChecksumSize*2 && len(strUH) != crypto.HashSize*2 {
		return ErrUnlockHashWrongLen
	}

	// Decode the unlock hash.
	var byteUnlockHash []byte
	var checksum []byte
	_, err := fmt.Sscanf(strUH[:crypto.HashSize*2], "%x", &byteUnlockHash)
	if err != nil {
		return err
	}
	// Decode the checksum, if provided.
	if len(strUH) == crypto.HashSize*2+UnlockHashChecksumSize*2 {
		_, err = fmt.Sscanf(strUH[crypto.HashSize*2:], "%x", &checksum)
		if err != nil {
			return err
		}

		// Verify the checksum - leave uh as-is unless the checksum is valid.
		expectedChecksum := crypto.HashBytes(byteUnlockHash)
		if !bytes.Equal(expectedChecksum[:UnlockHashChecksumSize], checksum) {
			return ErrInvalidUnlockHashChecksum
		}
	}
	copy(uh[:], byteUnlockHash[:])

	return nil
}

// Len implements the Len method of sort.Interface.
func (uhs UnlockHashSlice) Len() int {
	return len(uhs)
}

// Less implements the Less method of sort.Interface.
func (uhs UnlockHashSlice) Less(i, j int) bool {
	return bytes.Compare(uhs[i][:], uhs[j][:]) < 0
}

// Swap implements the Swap method of sort.Interface.
func (uhs UnlockHashSlice) Swap(i, j int) {
	uhs[i], uhs[j] = uhs[j], uhs[i]
}
<commit_msg>require unlock hashes to include the checksum<commit_after>package types

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/NebulousLabs/Sia/crypto"
)

// unlockhash.go contains the unlockhash alias along with usability methods
// such as String and an implementation of sort.Interface.

const (
	// UnlockHashChecksumSize is the size of the checksum used to verify
	// human-readable addresses. It is not a crypytographically secure
	// checksum, it's merely intended to prevent typos. 6 is chosen because it
	// brings the total size of the address to 38 bytes, leaving 2 bytes for
	// potential version additions in the future.
	UnlockHashChecksumSize = 6
)

// An UnlockHash is a specially constructed hash of the UnlockConditions type.
// "Locked" values can be unlocked by providing the UnlockConditions that hash
// to a given UnlockHash. See SpendConditions.UnlockHash for details on how the
// UnlockHash is constructed.
type UnlockHash crypto.Hash

type UnlockHashSlice []UnlockHash

// MarshalJSON is implemented on the unlock hash to always produce a hex string
// upon marshalling.
func (uh UnlockHash) MarshalJSON() ([]byte, error) {
	return json.Marshal(uh.String())
}

// UnmarshalJSON is implemented on the unlock hash to recover an unlock hash
// that has been encoded to a hex string.
func (uh *UnlockHash) UnmarshalJSON(b []byte) error {
	// Check the length of b.
	if len(b) != crypto.HashSize*2+UnlockHashChecksumSize*2+2 && len(b) != crypto.HashSize*2+2 {
		return ErrUnlockHashWrongLen
	}
	return uh.LoadString(string(b[1 : len(b)-1]))
}

// String returns the hex representation of the unlock hash as a string - this
// includes a checksum.
func (uh UnlockHash) String() string {
	uhChecksum := crypto.HashObject(uh)
	return fmt.Sprintf("%x%x", uh[:], uhChecksum[:UnlockHashChecksumSize])
}

// LoadString loads a hex representation (including checksum) of an unlock hash
// into an unlock hash object. An error is returned if the string is invalid or
// fails the checksum.
func (uh *UnlockHash) LoadString(strUH string) error {
	// Check the length of strUH.
	if len(strUH) != crypto.HashSize*2+UnlockHashChecksumSize*2 {
		return ErrUnlockHashWrongLen
	}

	// Decode the unlock hash.
	var byteUnlockHash []byte
	var checksum []byte
	_, err := fmt.Sscanf(strUH[:crypto.HashSize*2], "%x", &byteUnlockHash)
	if err != nil {
		return err
	}

	// Decode and verify the checksum.
	_, err = fmt.Sscanf(strUH[crypto.HashSize*2:], "%x", &checksum)
	if err != nil {
		return err
	}
	expectedChecksum := crypto.HashBytes(byteUnlockHash)
	if !bytes.Equal(expectedChecksum[:UnlockHashChecksumSize], checksum) {
		return ErrInvalidUnlockHashChecksum
	}

	copy(uh[:], byteUnlockHash[:])
	return nil
}

// Len implements the Len method of sort.Interface.
func (uhs UnlockHashSlice) Len() int {
	return len(uhs)
}

// Less implements the Less method of sort.Interface.
func (uhs UnlockHashSlice) Less(i, j int) bool {
	return bytes.Compare(uhs[i][:], uhs[j][:]) < 0
}

// Swap implements the Swap method of sort.Interface.
func (uhs UnlockHashSlice) Swap(i, j int) {
	uhs[i], uhs[j] = uhs[j], uhs[i]
}
<|endoftext|>