<commit_before>package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"text/tabwriter"
	"time"

	"github.com/coreos/updateservicectl/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/updateservicectl/Godeps/_workspace/src/github.com/coreos/go-omaha/omaha"
	update "github.com/coreos/updateservicectl/client/update/v1"
)

var (
	instanceFlags struct {
		groupId       StringFlag
		appId         StringFlag
		start         int64
		end           int64
		verbose       bool
		clientsPerApp int
		minSleep      int
		maxSleep      int
		errorRate     int
		OEM           string
		pingOnly      int
		version       string
	}

	cmdInstance = &Command{
		Name:    "instance",
		Usage:   "[OPTION]...",
		Summary: "Operations to view instances.",
		Subcommands: []*Command{
			cmdInstanceListUpdates,
			cmdInstanceListAppVersions,
			cmdInstanceFake,
		},
	}

	cmdInstanceListUpdates = &Command{
		Name:        "instance list-updates",
		Usage:       "[OPTION]...",
		Description: "Generates a list of instance updates.",
		Run:         instanceListUpdates,
	}

	cmdInstanceListAppVersions = &Command{
		Name:        "instance list-app-versions",
		Usage:       "[OPTION]...",
		Description: "Generates a list of apps/versions with instance count.",
		Run:         instanceListAppVersions,
	}

	cmdInstanceFake = &Command{
		Name:        "instance fake",
		Usage:       "[OPTION]...",
		Description: "Simulate multiple fake instances.",
		Run:         instanceFake,
	}
)

func init() {
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceFake.Flags.BoolVar(&instanceFlags.verbose, "verbose", false, "Print out the request bodies")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.clientsPerApp, "clients-per-app", 20, "Number of fake fents per appid.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.minSleep, "min-sleep", 1, "Minimum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.maxSleep, "max-sleep", 10, "Maximum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.errorRate, "errorrate", 1, "Chance of error (0-100)%.")
	cmdInstanceFake.Flags.StringVar(&instanceFlags.OEM, "oem", "fakeclient", "oem to report")
	// simulate reboot lock.
	cmdInstanceFake.Flags.IntVar(&instanceFlags.pingOnly, "ping-only", 0, "halt update and just send ping requests this many times.")
	cmdInstanceFake.Flags.Var(&instanceFlags.appId, "app-id", "Application ID to update.")
	instanceFlags.appId.required = true
	cmdInstanceFake.Flags.Var(&instanceFlags.groupId, "group-id", "Group ID to update.")
	instanceFlags.groupId.required = true
	cmdInstanceFake.Flags.StringVar(&instanceFlags.version, "version", "0.0.0", "Version to report.")
}

func instanceListUpdates(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Clientupdate.List()
	call.DateStart(instanceFlags.start)
	call.DateEnd(instanceFlags.end)
	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.groupId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	list, err := call.Do()

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

	fmt.Fprintln(out, "AppID\tClientID\tVersion\tLastSeen\tGroup\tOEM")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\n", cl.AppId,
			cl.ClientId, cl.Version, cl.LastSeen, cl.GroupId, cl.Oem)
	}
	out.Flush()
	return OK
}

func instanceListAppVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Appversion.List()

	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.appId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	if instanceFlags.start != 0 {
		call.DateStart(instanceFlags.start)
	}

	if instanceFlags.end != 0 {
		call.DateEnd(instanceFlags.end)
	}

	list, err := call.Do()

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

	fmt.Fprintln(out, "AppID\tGroupID\tVersion\tClients")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%d\n", cl.AppId, cl.GroupId, cl.Version, cl.Count)
	}
	out.Flush()
	return OK
}

type serverConfig struct {
	server string
}

type Client struct {
	Id             string
	SessionId      string
	Version        string
	AppId          string
	Track          string
	config         *serverConfig
	errorRate      int
	pingsRemaining int
}

func (c *Client) Log(format string, v ...interface{}) {
	format = c.Id + ": " + format
	fmt.Printf(format, v...)
}

func (c *Client) OmahaRequest(otype, result string, updateCheck, isPing bool) *omaha.Request {
	req := omaha.NewRequest("lsb", "CoreOS", "", "")
	app := req.AddApp(c.AppId, c.Version)
	app.MachineID = c.Id
	app.BootId = c.SessionId
	app.Track = c.Track
	app.OEM = instanceFlags.OEM

	if updateCheck {
		app.AddUpdateCheck()
	}

	if isPing {
		app.AddPing()
		app.Ping.LastReportDays = "1"
		app.Ping.Status = "1"
	}

	if otype != "" {
		event := app.AddEvent()
		event.Type = otype
		event.Result = result
		if result == "0" {
			event.ErrorCode = "2000"
		} else {
			event.ErrorCode = ""
		}
	}

	return req
}

func (c *Client) MakeRequest(otype, result string, updateCheck, isPing bool) (*omaha.Response, error) {
	client := &http.Client{}
	req := c.OmahaRequest(otype, result, updateCheck, isPing)
	raw, err := xml.MarshalIndent(req, "", " ")
	if err != nil {
		return nil, err
	}

	resp, err := client.Post(c.config.server+"/v1/update/", "text/xml", bytes.NewReader(raw))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	oresp := new(omaha.Response)
	err = xml.NewDecoder(resp.Body).Decode(oresp)
	if err != nil {
		return nil, err
	}

	if instanceFlags.verbose {
		raw, _ := xml.MarshalIndent(req, "", " ")
		c.Log("request: %s\n", string(raw))
		raw, _ = xml.MarshalIndent(oresp, "", " ")
		c.Log("response: %s\n", string(raw))
	}

	return oresp, nil
}

func (c *Client) SetVersion(resp *omaha.Response) {
	// A field can potentially be nil.
	defer func() {
		if err := recover(); err != nil {
			c.Log("%s: error setting version: %v\n", c.Id, err)
		}
	}()

	uc := resp.Apps[0].UpdateCheck
	if uc.Status != "ok" {
		c.Log("%s\n", uc.Status)
		return
	}

	randFailRequest := func(eventType, eventResult string) (failed bool, err error) {
		if rand.Intn(100) <= c.errorRate {
			eventType = "3"
			eventResult = "0"
			failed = true
		}
		_, err = c.MakeRequest(eventType, eventResult, false, false)
		return
	}

	requests := [][]string{
		[]string{"13", "1"}, // downloading
		[]string{"14", "1"}, // downloaded
		[]string{"3", "1"},  // installed
	}

	for i, r := range requests {
		if i > 0 {
			time.Sleep(1 * time.Second)
		}
		failed, err := randFailRequest(r[0], r[1])
		if failed {
			log.Printf("failed to update in eventType: %s, eventResult: %s. Retrying.", r[0], r[1])
			time.Sleep(time.Second * time.Duration(instanceFlags.minSleep))
			c.MakeRequest(r[0], r[1], false, false)
			return
		}
		if err != nil {
			log.Println(err)
			return
		}
	}

	// simulate reboot lock for a while
	for c.pingsRemaining > 0 {
		c.MakeRequest("", "", false, true)
		c.pingsRemaining--
		time.Sleep(1 * time.Second)
	}

	c.Log("updated from %s to %s\n", c.Version, uc.Manifest.Version)

	c.Version = uc.Manifest.Version

	_, err := c.MakeRequest("3", "2", false, false) // Send complete with new version.
	if err != nil {
		log.Println(err)
	}

	c.SessionId = uuid.New()
}

// Sleep between n and m seconds
func (c *Client) Loop(n, m int) {
	for {
		randSleep(n, m)

		resp, err := c.MakeRequest("3", "2", true, false)
		if err != nil {
			log.Println(err)
			continue
		}
		c.SetVersion(resp)
	}
}

// Sleeps randomly between n and m seconds.
func randSleep(n, m int) {
	r := m
	if m-n > 0 {
		r = rand.Intn(m-n) + n
	}
	time.Sleep(time.Duration(r) * time.Second)
}

func instanceFake(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil || instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	conf := &serverConfig{
		server: globalFlags.Server,
	}

	for i := 0; i < instanceFlags.clientsPerApp; i++ {
		c := &Client{
			Id:             fmt.Sprintf("{fake-client-%03d}", i),
			SessionId:      uuid.New(),
			Version:        instanceFlags.version,
			AppId:          instanceFlags.appId.String(),
			Track:          instanceFlags.groupId.String(),
			config:         conf,
			errorRate:      instanceFlags.errorRate,
			pingsRemaining: instanceFlags.pingOnly,
		}
		go c.Loop(instanceFlags.minSleep, instanceFlags.maxSleep)
	}

	// run forever
	wait := make(chan bool)
	<-wait
	return OK
}
<commit_msg>instance: flag to simulate forced updates<commit_after>package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"text/tabwriter"
	"time"

	"github.com/coreos/updateservicectl/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/updateservicectl/Godeps/_workspace/src/github.com/coreos/go-omaha/omaha"
	update "github.com/coreos/updateservicectl/client/update/v1"
)

var (
	instanceFlags struct {
		groupId       StringFlag
		appId         StringFlag
		start         int64
		end           int64
		verbose       bool
		clientsPerApp int
		minSleep      int
		maxSleep      int
		errorRate     int
		OEM           string
		pingOnly      int
		version       string
		forceUpdate   bool
	}

	cmdInstance = &Command{
		Name:    "instance",
		Usage:   "[OPTION]...",
		Summary: "Operations to view instances.",
		Subcommands: []*Command{
			cmdInstanceListUpdates,
			cmdInstanceListAppVersions,
			cmdInstanceFake,
		},
	}

	cmdInstanceListUpdates = &Command{
		Name:        "instance list-updates",
		Usage:       "[OPTION]...",
		Description: "Generates a list of instance updates.",
		Run:         instanceListUpdates,
	}

	cmdInstanceListAppVersions = &Command{
		Name:        "instance list-app-versions",
		Usage:       "[OPTION]...",
		Description: "Generates a list of apps/versions with instance count.",
		Run:         instanceListAppVersions,
	}

	cmdInstanceFake = &Command{
		Name:        "instance fake",
		Usage:       "[OPTION]...",
		Description: "Simulate multiple fake instances.",
		Run:         instanceFake,
	}
)

func init() {
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListUpdates.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListUpdates.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.groupId, "group-id", "Group id")
	cmdInstanceListAppVersions.Flags.Var(&instanceFlags.appId, "app-id", "App id")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.start, "start", 0, "Start date filter")
	cmdInstanceListAppVersions.Flags.Int64Var(&instanceFlags.end, "end", 0, "End date filter")

	cmdInstanceFake.Flags.BoolVar(&instanceFlags.verbose, "verbose", false, "Print out the request bodies")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.clientsPerApp, "clients-per-app", 20, "Number of fake fents per appid.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.minSleep, "min-sleep", 1, "Minimum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.maxSleep, "max-sleep", 10, "Maximum time between update checks.")
	cmdInstanceFake.Flags.IntVar(&instanceFlags.errorRate, "errorrate", 1, "Chance of error (0-100)%.")
	cmdInstanceFake.Flags.StringVar(&instanceFlags.OEM, "oem", "fakeclient", "oem to report")
	// simulate reboot lock.
	cmdInstanceFake.Flags.IntVar(&instanceFlags.pingOnly, "ping-only", 0, "halt update and just send ping requests this many times.")
	cmdInstanceFake.Flags.Var(&instanceFlags.appId, "app-id", "Application ID to update.")
	instanceFlags.appId.required = true
	cmdInstanceFake.Flags.Var(&instanceFlags.groupId, "group-id", "Group ID to update.")
	instanceFlags.groupId.required = true
	cmdInstanceFake.Flags.StringVar(&instanceFlags.version, "version", "0.0.0", "Version to report.")
	cmdInstanceFake.Flags.BoolVar(&instanceFlags.forceUpdate, "force-update", false, "Force updates regardless of rate limiting")
}

func instanceListUpdates(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Clientupdate.List()
	call.DateStart(instanceFlags.start)
	call.DateEnd(instanceFlags.end)
	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.groupId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	list, err := call.Do()

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

	fmt.Fprintln(out, "AppID\tClientID\tVersion\tLastSeen\tGroup\tOEM")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\n", cl.AppId,
			cl.ClientId, cl.Version, cl.LastSeen, cl.GroupId, cl.Oem)
	}
	out.Flush()
	return OK
}

func instanceListAppVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	call := service.Appversion.List()

	if instanceFlags.groupId.Get() != nil {
		call.GroupId(instanceFlags.groupId.String())
	}
	if instanceFlags.appId.Get() != nil {
		call.AppId(instanceFlags.appId.String())
	}
	if instanceFlags.start != 0 {
		call.DateStart(instanceFlags.start)
	}

	if instanceFlags.end != 0 {
		call.DateEnd(instanceFlags.end)
	}

	list, err := call.Do()

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

	fmt.Fprintln(out, "AppID\tGroupID\tVersion\tClients")
	for _, cl := range list.Items {
		fmt.Fprintf(out, "%s\t%s\t%s\t%d\n", cl.AppId, cl.GroupId, cl.Version, cl.Count)
	}
	out.Flush()
	return OK
}

type serverConfig struct {
	server string
}

type Client struct {
	Id             string
	SessionId      string
	Version        string
	AppId          string
	Track          string
	config         *serverConfig
	errorRate      int
	pingsRemaining int
	forceUpdate    bool
}

func (c *Client) Log(format string, v ...interface{}) {
	format = c.Id + ": " + format
	fmt.Printf(format, v...)
}

func (c *Client) OmahaRequest(otype, result string, updateCheck, isPing bool) *omaha.Request {
	req := omaha.NewRequest("lsb", "CoreOS", "", "")
	app := req.AddApp(c.AppId, c.Version)
	app.MachineID = c.Id
	app.BootId = c.SessionId
	app.Track = c.Track
	app.OEM = instanceFlags.OEM
	if c.forceUpdate {
		req.InstallSource = "ondemandupdate"
	} else {
		req.InstallSource = "scheduler"
	}

	if updateCheck {
		app.AddUpdateCheck()
	}

	if isPing {
		app.AddPing()
		app.Ping.LastReportDays = "1"
		app.Ping.Status = "1"
	}

	if otype != "" {
		event := app.AddEvent()
		event.Type = otype
		event.Result = result
		if result == "0" {
			event.ErrorCode = "2000"
		} else {
			event.ErrorCode = ""
		}
	}

	return req
}

func (c *Client) MakeRequest(otype, result string, updateCheck, isPing bool) (*omaha.Response, error) {
	client := &http.Client{}
	req := c.OmahaRequest(otype, result, updateCheck, isPing)
	raw, err := xml.MarshalIndent(req, "", " ")
	if err != nil {
		return nil, err
	}

	resp, err := client.Post(c.config.server+"/v1/update/", "text/xml", bytes.NewReader(raw))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	oresp := new(omaha.Response)
	err = xml.NewDecoder(resp.Body).Decode(oresp)
	if err != nil {
		return nil, err
	}

	if instanceFlags.verbose {
		raw, _ := xml.MarshalIndent(req, "", " ")
		c.Log("request: %s\n", string(raw))
		raw, _ = xml.MarshalIndent(oresp, "", " ")
		c.Log("response: %s\n", string(raw))
	}

	return oresp, nil
}

func (c *Client) SetVersion(resp *omaha.Response) {
	// A field can potentially be nil.
	defer func() {
		if err := recover(); err != nil {
			c.Log("%s: error setting version: %v\n", c.Id, err)
		}
	}()

	uc := resp.Apps[0].UpdateCheck
	if uc.Status != "ok" {
		c.Log("%s\n", uc.Status)
		return
	}

	randFailRequest := func(eventType, eventResult string) (failed bool, err error) {
		if rand.Intn(100) <= c.errorRate {
			eventType = "3"
			eventResult = "0"
			failed = true
		}
		_, err = c.MakeRequest(eventType, eventResult, false, false)
		return
	}

	requests := [][]string{
		[]string{"13", "1"}, // downloading
		[]string{"14", "1"}, // downloaded
		[]string{"3", "1"},  // installed
	}

	for i, r := range requests {
		if i > 0 {
			time.Sleep(1 * time.Second)
		}
		failed, err := randFailRequest(r[0], r[1])
		if failed {
			log.Printf("failed to update in eventType: %s, eventResult: %s. Retrying.", r[0], r[1])
			time.Sleep(time.Second * time.Duration(instanceFlags.minSleep))
			c.MakeRequest(r[0], r[1], false, false)
			return
		}
		if err != nil {
			log.Println(err)
			return
		}
	}

	// simulate reboot lock for a while
	for c.pingsRemaining > 0 {
		c.MakeRequest("", "", false, true)
		c.pingsRemaining--
		time.Sleep(1 * time.Second)
	}

	c.Log("updated from %s to %s\n", c.Version, uc.Manifest.Version)

	c.Version = uc.Manifest.Version

	_, err := c.MakeRequest("3", "2", false, false) // Send complete with new version.
	if err != nil {
		log.Println(err)
	}

	c.SessionId = uuid.New()
}

// Sleep between n and m seconds
func (c *Client) Loop(n, m int) {
	for {
		randSleep(n, m)

		resp, err := c.MakeRequest("3", "2", true, false)
		if err != nil {
			log.Println(err)
			continue
		}
		c.SetVersion(resp)
	}
}

// Sleeps randomly between n and m seconds.
func randSleep(n, m int) {
	r := m
	if m-n > 0 {
		r = rand.Intn(m-n) + n
	}
	time.Sleep(time.Duration(r) * time.Second)
}

func instanceFake(args []string, service *update.Service, out *tabwriter.Writer) int {
	if instanceFlags.appId.Get() == nil || instanceFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	conf := &serverConfig{
		server: globalFlags.Server,
	}

	for i := 0; i < instanceFlags.clientsPerApp; i++ {
		c := &Client{
			Id:             fmt.Sprintf("{fake-client-%03d}", i),
			SessionId:      uuid.New(),
			Version:        instanceFlags.version,
			AppId:          instanceFlags.appId.String(),
			Track:          instanceFlags.groupId.String(),
			config:         conf,
			errorRate:      instanceFlags.errorRate,
			pingsRemaining: instanceFlags.pingOnly,
			forceUpdate:    instanceFlags.forceUpdate,
		}
		go c.Loop(instanceFlags.minSleep, instanceFlags.maxSleep)
	}

	// run forever
	wait := make(chan bool)
	<-wait
	return OK
}
<|endoftext|>


<commit_before>package revel

import (
	"fmt"
	"runtime/debug"
	"strings"
)

// An error description, used as an argument to the error template.
type Error struct {
	SourceType               string   // The type of source that failed to build.
	Title, Path, Description string   // Description of the error, as presented to the user.
	Line, Column             int      // Where the error was encountered.
	SourceLines              []string // The entire source file, split into lines.
	Stack                    string   // The raw stack trace string from debug.Stack().
	MetaError                string   // Error that occurred producing the error page.
}

// An object to hold the per-source-line details.
type sourceLine struct {
	Source  string
	Line    int
	IsError bool
}

// Find the deepest stack from in uesr code and provide a code listing of
// that, on the line that eventually triggered the panic.  Returns nil if no
// relevant stack frame can be found.
func NewErrorFromPanic(err interface{}) *Error {

	// Parse the filename and line from the originating line of app code.
	// /Users/robfig/code/gocode/src/revel/samples/booking/app/controllers/hotels.go:191 (0x44735)
	stack := string(debug.Stack())
	frame, basePath := findRelevantStackFrame(stack)
	if frame == -1 {
		return nil
	}

	stack = stack[frame:]
	stackElement := stack[:strings.Index(stack, "\n")]
	colonIndex := strings.LastIndex(stackElement, ":")
	filename := stackElement[:colonIndex]
	var line int
	fmt.Sscan(stackElement[colonIndex+1:], &line)

	// Show an error page.
	description := "Unspecified error"
	if err != nil {
		description = fmt.Sprint(err)
	}
	return &Error{
		Title:       "Panic",
		Path:        filename[len(basePath):],
		Line:        line,
		Description: description,
		SourceLines: MustReadLines(filename),
		Stack:       stack,
	}
}

// Construct a plaintext version of the error, taking account that fields are optionally set.
// Returns e.g. Compilation Error (in views/header.html:51): expected right delim in end; got "}"
func (e *Error) Error() string {
	loc := ""
	if e.Path != "" {
		line := ""
		if e.Line != 0 {
			line = fmt.Sprintf(":%d", e.Line)
		}
		loc = fmt.Sprintf("(in %s%s)", e.Path, line)
	}
	header := loc
	if e.Title != "" {
		if loc != "" {
			header = fmt.Sprintf("%s %s: ", e.Title, loc)
		} else {
			header = fmt.Sprintf("%s: ", e.Title)
		}
	}
	return fmt.Sprintf("%s%s", header, e.Description)
}

// Returns a snippet of the source around where the error occurred.
func (e *Error) ContextSource() []sourceLine {
	if e.SourceLines == nil {
		return nil
	}
	start := (e.Line - 1) - 5
	if start < 0 {
		start = 0
	}
	end := (e.Line - 1) + 5
	if end > len(e.SourceLines) {
		end = len(e.SourceLines)
	}

	var lines []sourceLine = make([]sourceLine, end-start)
	for i, src := range e.SourceLines[start:end] {
		fileLine := start + i + 1
		lines[i] = sourceLine{src, fileLine, fileLine == e.Line}
	}
	return lines
}

// Return the character index of the first relevant stack frame, or -1 if none were found.
// Additionally it returns the base path of the tree in which the identified code resides.
func findRelevantStackFrame(stack string) (int, string) {
	if frame := strings.Index(stack, BasePath); frame != -1 {
		return frame, BasePath
	}
	for _, module := range Modules {
		if frame := strings.Index(stack, module.Path); frame != -1 {
			return frame, module.Path
		}
	}
	return -1, ""
}
<commit_msg>fix typo of user<commit_after>package revel

import (
	"fmt"
	"runtime/debug"
	"strings"
)

// An error description, used as an argument to the error template.
type Error struct {
	SourceType               string   // The type of source that failed to build.
	Title, Path, Description string   // Description of the error, as presented to the user.
	Line, Column             int      // Where the error was encountered.
	SourceLines              []string // The entire source file, split into lines.
	Stack                    string   // The raw stack trace string from debug.Stack().
	MetaError                string   // Error that occurred producing the error page.
}

// An object to hold the per-source-line details.
type sourceLine struct {
	Source  string
	Line    int
	IsError bool
}

// Find the deepest stack from in user code and provide a code listing of
// that, on the line that eventually triggered the panic.  Returns nil if no
// relevant stack frame can be found.
func NewErrorFromPanic(err interface{}) *Error {

	// Parse the filename and line from the originating line of app code.
	// /Users/robfig/code/gocode/src/revel/samples/booking/app/controllers/hotels.go:191 (0x44735)
	stack := string(debug.Stack())
	frame, basePath := findRelevantStackFrame(stack)
	if frame == -1 {
		return nil
	}

	stack = stack[frame:]
	stackElement := stack[:strings.Index(stack, "\n")]
	colonIndex := strings.LastIndex(stackElement, ":")
	filename := stackElement[:colonIndex]
	var line int
	fmt.Sscan(stackElement[colonIndex+1:], &line)

	// Show an error page.
	description := "Unspecified error"
	if err != nil {
		description = fmt.Sprint(err)
	}
	return &Error{
		Title:       "Panic",
		Path:        filename[len(basePath):],
		Line:        line,
		Description: description,
		SourceLines: MustReadLines(filename),
		Stack:       stack,
	}
}

// Construct a plaintext version of the error, taking account that fields are optionally set.
// Returns e.g. Compilation Error (in views/header.html:51): expected right delim in end; got "}"
func (e *Error) Error() string {
	loc := ""
	if e.Path != "" {
		line := ""
		if e.Line != 0 {
			line = fmt.Sprintf(":%d", e.Line)
		}
		loc = fmt.Sprintf("(in %s%s)", e.Path, line)
	}
	header := loc
	if e.Title != "" {
		if loc != "" {
			header = fmt.Sprintf("%s %s: ", e.Title, loc)
		} else {
			header = fmt.Sprintf("%s: ", e.Title)
		}
	}
	return fmt.Sprintf("%s%s", header, e.Description)
}

// Returns a snippet of the source around where the error occurred.
func (e *Error) ContextSource() []sourceLine {
	if e.SourceLines == nil {
		return nil
	}
	start := (e.Line - 1) - 5
	if start < 0 {
		start = 0
	}
	end := (e.Line - 1) + 5
	if end > len(e.SourceLines) {
		end = len(e.SourceLines)
	}

	var lines []sourceLine = make([]sourceLine, end-start)
	for i, src := range e.SourceLines[start:end] {
		fileLine := start + i + 1
		lines[i] = sourceLine{src, fileLine, fileLine == e.Line}
	}
	return lines
}

// Return the character index of the first relevant stack frame, or -1 if none were found.
// Additionally it returns the base path of the tree in which the identified code resides.
func findRelevantStackFrame(stack string) (int, string) {
	if frame := strings.Index(stack, BasePath); frame != -1 {
		return frame, BasePath
	}
	for _, module := range Modules {
		if frame := strings.Index(stack, module.Path); frame != -1 {
			return frame, module.Path
		}
	}
	return -1, ""
}
<|endoftext|>


<commit_before>// Copyright 2016 The Linux Foundation
//
// 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 specs

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-rc4-dev"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<commit_msg>version: release v1.0.0-rc5<commit_after>// Copyright 2016 The Linux Foundation
//
// 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 specs

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-rc5"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<|endoftext|>


<commit_before>package TM_EC

import (
	"strings"
)

type Errors struct {
	errors []error
}

func (errs Errors) Error() string {
	var errors []string
	for _, err := range errs.errors {
		errors = append(errors, err.Error())
	}

	return strings.Join(errors, ";")
}

func (errs *Errors) AddError(errors ...error) {
	for _, err := range errs.errors {
		if err != nil {
			if e, ok := err.(errorsInterface); ok {
				errs.errors = append(errs.errors, e.GetErrors()...)
			} else {
				errs.errors = append(errs.errors, err)
			}
		}
	}
}

func (errs Errors) HasError() bool {
	return len(errs.errors) != 0
}

//	'getErrors'
func (errs Errors) getErrors() []error {
	return errs.getErrors()
}

type errorsInterface interface {
	GetErrors() []error
}
<commit_msg>fix errors.go<commit_after>package TM_EC

import (
	"strings"
)

type Errors struct {
	errors []error
}

func (errs Errors) Error() string {
	var errors []string
	for _, err := range errs.errors {
		errors = append(errors, err.Error())
	}

	return strings.Join(errors, ";")
}

func (errs *Errors) AddError(errors ...error) {
	for _, err := range errs.errors {
		if err != nil {
			if e, ok := err.(errorsInterface); ok {
				errs.errors = append(errs.errors, e.GetErrors()...)
			} else {
				errs.errors = append(errs.errors, err)
			}
		}
	}
}

func (errs Errors) HasError() bool {
	return len(errs.errors) != 0
}

//	'GetErrors'
func (errs Errors) GetErrors() []error {
	return errs.getErrors()
}

type errorsInterface interface {
	GetErrors() []error
}
<|endoftext|>


<commit_before>// Package ircudf implements the Internet Realy Chat (IRC) protocol
// as defined in rfc1459 and provides basic IRC functions
// Website: http://tools.ietf.org/html/rfc1459.html
package ircudf

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"time"
	"strings"
)

type Server struct {
	server    string
	sendqueue chan string
	conn      net.Conn
	/* USER */
	nickname  string
	username string
	realname string
	/* CHANNEL */
}

// Create sets the server address and port. Example:
// freenode := ircudf.Connect("irc.freenode.org:6667")
func Create(addr string, nickname string) Server {
	ref := Server{}
	ref.server = addr
	ref.sendqueue = make(chan string)
	ref.nickname = nickname
	debug("Create:", ref.server, addr, "\n")
	return ref
}

// Connect establishes a connection to the server.
// If no parameter is given the default timeout (5 sec) will be used.
func (sock *Server) Connect(timeout ...int) {
	wait := 5 * time.Second
	if len(timeout) >= 1 {
		wait = time.Duration(timeout[0]) * time.Second
	}

	conn, err := net.DialTimeout("tcp", sock.server, wait)
	sock.conn = conn
	errcheck(err)
	debug("Connect:", sock.server, "\n")
}

// Receive receives new messages from the server and forwards them to parse().
func (sock *Server) Receive() {

	go func() {

		debug("Receive:", sock.server, "\n")
		reader := bufio.NewReader(sock.conn)
		go sock.sendroutine()

		for {
			line, err := reader.ReadString('\n')
			errcheck(err)
			debug("<-", line)
			sock.parse(strings.Trim(line, "\r\n"))	//Remove \r\n for easier parsing
		}

	}()

	time.Sleep(time.Second)
	sock.nick(sock.nickname)
	sock.user(sock.realname, "0", "0", sock.realname)
}

// parse parses incoming messages from the server and triggers predefined events.
func (sock *Server) parse(line string) {
	split := strings.SplitN(line, " ", 4)
	split = append(split, make([]string, 4-len(split), 4-len(split))...)

	switch true {
	case split[0] == "PING":
		sock.pong(split[1]) //Ping e.g.: PING :B97B6379
	case split[1] == "PRIVMSG" && split[3][1:] == "!ping":
		sock.privmsg(split[2], "!pong")
	case split[1] == "376":
		sock.join("#irccs")
	}
}

// nick sets or changes the nickname. The IRC server might reply an errorcode,
// if the requsted nickname is not valid or in use.
func (sock *Server) nick(nickname string) {
	sock.nickname = nickname
	sock.send("NICK " + nickname)
}

// user specifies the userdata at the beginning of a new connection.
// Servername and hostname are likely to be ignored by the IRC server.
// Scheme: (Nickname!Username@Hostname): Real Name
func (sock *Server) user(username, hostname, servername, realname string) {
	sock.send("USER " + username + " " + hostname + " " + servername + " :" + realname)
}

// join joins the specified channel(s). Multiple channels need to be
// seperated by a colon. */
func (sock *Server) join(channel string) {
	sock.send("JOIN " + channel )
}

// pong answers a ping request with a previously received reply string.
func (sock *Server) pong(reply string) {
	sock.send("PONG " + reply)
}

// privmsg sends a private message to a user or a channel.
// The parameter user can also contain multiple receivers seperated by a colon.
func (sock *Server) privmsg(user string, message string, ) {
	sock.send("PRIVMSG " + user + " :" + message)
}

// send adds the message to the sendqueue (similar to a FIFO stack)
func (sock *Server) send(message string) {
	go func() {
		sock.sendqueue <- message + "\n"
	}()
}

// sendroutine processes the sendqueue and sends the messages to the server.
// sendrouting must be executed as goroutine!
func (sock *Server) sendroutine() {
	for {
		smsg := <-sock.sendqueue
		fmt.Fprint(sock.conn, smsg)
		debug("->", smsg)
	}
}

// DEBUG FUNCTIONS

func errcheck(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func debug(s ...interface{}) {
	fmt.Print(s...) //log.Print adds timestamp
}
<commit_msg>small fixes<commit_after>// Package ircudf implements parts of the Internet Realy Chat (IRC) protocol
// as defined in rfc1459 (http://tools.ietf.org/html/rfc1459.html)
// and provides basic IRC functions
package ircudf

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"time"
	"strings"
)

type Server struct {
	server    string		//Server address
	sendqueue chan string	//Message queue
	conn      net.Conn		//Server connection

	nickname  string		//User nickname
	username string			//User username
	realname string			//User realname
}

// Create sets the server address and port. Example:
// freenode := ircudf.Connect("irc.freenode.org:6667")
func Create(addr, nickname, username, realname string) Server {
	ref := Server{server: addr, sendqueue: make(chan string),
	nickname: nickname, username: username, realname: realname}

	debug("Create:", ref.server, addr, "\n")
	return ref
}

// Connect establishes a connection to the server.
// If no parameter is given the default timeout (5 sec) will be used.
func (sock *Server) Connect(timeout ...int) {
	wait := 5 * time.Second
	if len(timeout) >= 1 {
		wait = time.Duration(timeout[0]) * time.Second
	}

	conn, err := net.DialTimeout("tcp", sock.server, wait)
	sock.conn = conn
	errcheck(err)	//ADD: RECONNECT
	debug("Connect:", sock.server, "\n")
}

// Receive receives new messages from the server and forwards them to parse().
func (sock *Server) Receive() {

	go func() {
		debug("Receive:", sock.server, "\n")
		reader := bufio.NewReader(sock.conn)
		go sock.sendroutine()

		for {
			line, err := reader.ReadString('\n')
			errcheck(err)
			debug("<-", line)
			sock.parse(strings.Trim(line, "\r\n"))	//Remove \r\n for easier parsing
		}

	}()

	time.Sleep(time.Second)
	sock.Nick(sock.nickname)
	sock.user(sock.username, "0", "0" , sock.realname)
}

// parse parses incoming messages from the server and triggers predefined events.
func (sock *Server) parse(line string) {
	split := strings.SplitN(line, " ", 4)
	split = append(split, make([]string, 4-len(split), 4-len(split))...)

	switch true {
	case split[0] == "PING":
		sock.pong(split[1]) //Ping e.g.: PING :B97B6379
	case split[1] == "PRIVMSG" && split[3][1:] == "!ping":
		sock.Privmsg(split[2], "!pong")
	case split[1] == "376":
		sock.Join("#irccs")
	}
}

// Nick sets or changes the nickname. The IRC server might reply an errorcode,
// if the requsted nickname is not valid or in use.
func (sock *Server) Nick(nickname string) {
	sock.nickname = nickname
	sock.Send("NICK " + nickname)
}

// user specifies the userdata at the beginning of a new connection.
// Servername and hostname are likely to be ignored by the IRC server.
// Scheme: (Nickname!Username@Hostname): Real Name
func (sock *Server) user(username, hostname, servername, realname string) {
	sock.Send("USER " + username + " " + hostname + " " + servername + " :" + realname)
}

// Join joins the specified channel(s). Multiple channels need to be
// seperated by a colon. */
func (sock *Server) Join(channel string) {
	sock.Send("JOIN " + channel )
}

// pong answers a ping request with a previously received reply string.
func (sock *Server) pong(reply string) {
	sock.Send("PONG " + reply)
}

// Privmsg sends a private message to a user or a channel.
// The parameter user can also contain multiple receivers seperated by a colon.
func (sock *Server) Privmsg(user string, message string, ) {
	sock.Send("PRIVMSG " + user + " :" + message)
}

// Send adds the message to the RAW Message queue (similar to a FIFO stack)
func (sock *Server) Send(message string) {
	go func() {
		sock.sendqueue <- message + "\n"
	}()
}

// sendroutine processes the sendqueue and sends the messages to the server.
// sendrouting must be executed as goroutine!
func (sock *Server) sendroutine() {
	for {
		smsg := <-sock.sendqueue
		fmt.Fprint(sock.conn, smsg)
		debug("->", smsg)
	}
}

// DEBUG FUNCTIONS

func errcheck(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func debug(s ...interface{}) {
	fmt.Print(s...) //log.Print adds timestamp
}
<|endoftext|>


<commit_before>// Package ircudf implements parts of the Internet Realy Chat (IRC) protocol
// as defined in rfc1459 (http://tools.ietf.org/html/rfc1459.html)
// and provides basic IRC functions
package ircudf

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
)

type Server struct { //IRC Server
	server    string      //Server address
	sendqueue chan string //Message queue
	conn      net.Conn    //Server connection

	nickname string //User nickname
	username string //User username
	realname string //User realname
}

var ( // Events can be changed to custom functions
	EventOnJoin    func(string, string)         // channel, user
	EventOnPart    func(string, string, string) // channel, user, message
	EventOnPrivmsg func(string, string, string) // channel, user, message
	EventOnNotice  func(string, string, string) // channel, user, message
	EventOnReply   func(string, string, string) // number, name, reply
)

// Create sets the server address and port. Example:
// freenode := ircudf.Connect("irc.freenode.org:6667")
func Create(addr, nickname, username, realname string) Server {
	ref := Server{server: addr, sendqueue: make(chan string),
		nickname: nickname, username: username, realname: realname}

	debug("Create:", ref.server, addr, "\n")
	return ref
}

// Connect establishes a connection to the server.
// If no parameter is given the default timeout (5 sec) will be used.
func (sock *Server) Connect(timeout ...int) {
	wait := 5 * time.Second
	if len(timeout) >= 1 {
		wait = time.Duration(timeout[0]) * time.Second
	}

	conn, err := net.DialTimeout("tcp", sock.server, wait)
	sock.conn = conn
	errcheck(err) //ADD: RECONNECT
	debug("Connect:", sock.server, "\n")
}

// Receive receives new messages from the server and forwards them to parse().
func (sock *Server) Receive() {

	go func() {
		debug("Receive:", sock.server, "\n")
		reader := bufio.NewReader(sock.conn)
		sock.sendroutine() //Non-Blocking

		for {
			line, err := reader.ReadString('\n')
			errcheck(err)
			debug("<-", line)
			sock.parse(strings.Trim(line, "\r\n")) //Remove \r\n for easier parsing
		}

	}()

	time.Sleep(time.Second)
	sock.Nick(sock.nickname)
	sock.user(sock.username, "0", "0", sock.realname)
}

// parse parses incoming messages from the server and triggers predefined events.
func (sock *Server) parse(line string) {
	split := strings.SplitN(line, " ", 4)
	split = append(split, make([]string, 4-len(split), 4-len(split))...)

	switch true {
	case split[0] == "PING":
		sock.pong(split[1]) //Ping e.g.: PING :B97B6379
	case split[1] == "PRIVMSG" && split[3][1:] == "!ping":
		sock.Privmsg(split[2], "!pong")
	case split[1] == "376":
		sock.Join("#irccs")
	}
}

// Nick sets or changes the nickname. The IRC server might reply an errorcode,
// if the requsted nickname is not valid or in use.
func (sock *Server) Nick(nickname string) {
	sock.nickname = nickname
	sock.Send("NICK " + nickname)
}

// user specifies the userdata at the beginning of a new connection.
// Servername and hostname are likely to be ignored by the IRC server.
// Scheme: (Nickname!Username@Hostname): Real Name
func (sock *Server) user(username, hostname, servername, realname string) {
	sock.Send("USER " + username + " " + hostname + " " + servername + " :" + realname)
}

// Join joins the specified channel(s). Multiple channels need to be
// seperated by a colon. */
func (sock *Server) Join(channel string) {
	sock.Send("JOIN " + channel)
}

// pong answers a ping request with a previously received reply string.
func (sock *Server) pong(reply string) {
	sock.Send("PONG " + reply)
}

// Privmsg sends a private message to a user or a channel.
// The parameter user can also contain multiple receivers seperated by a colon.
func (sock *Server) Privmsg(user string, message string) {
	sock.Send("PRIVMSG " + user + " :" + message)
}

// Send adds the message to the RAW Message queue (similar to a FIFO stack)
func (sock *Server) Send(message string) {
	go func() {
		sock.sendqueue <- message + "\n"
	}()
}

// sendroutine processes the sendqueue and sends the messages to the server.
// sendrouting must be executed as goroutine!
func (sock *Server) sendroutine() {
	go func() {
		for {
			smsg := <-sock.sendqueue
			fmt.Fprint(sock.conn, smsg)
			debug("->", smsg)
		}
	}()
}

// DEBUG FUNCTIONS

func errcheck(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func debug(s ...interface{}) {
	fmt.Print(s...) //log.Print adds timestamp
}
<commit_msg>io.WriteString<commit_after>// Package ircudf implements parts of the Internet Realy Chat (IRC) protocol
// as defined in rfc1459 (http://tools.ietf.org/html/rfc1459.html)
// and provides basic IRC functions
package ircudf

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
	"io"
)

type Server struct { //IRC Server
	server    string      //Server address
	sendqueue chan string //Message queue
	conn      net.Conn    //Server connection

	nickname string //User nickname
	username string //User username
	realname string //User realname
}

var ( // Events can be changed to custom functions
	EventOnJoin    func(string, string)         // channel, user
	EventOnPart    func(string, string, string) // channel, user, message
	EventOnPrivmsg func(string, string, string) // channel, user, message
	EventOnNotice  func(string, string, string) // channel, user, message
	EventOnReply   func(string, string, string) // number, name, reply
)

// Create sets the server address and user information. Example:
// 	freenode := ircudf.Connect("irc.freenode.org:6667", "jmiller", "jmiller, "John Miller")
func Create(addr, nickname, username, realname string) Server {
	ref := Server{server: addr, sendqueue: make(chan string),
		nickname: nickname, username: username, realname: realname}

	debug("Create:", ref.server, addr, "\n")
	return ref
}

// Connect establishes a connection to the server.
// If no parameter is given the default timeout (5 sec) will be used.
func (sock *Server) Connect(timeout ...int) {
	wait := 5 * time.Second
	if len(timeout) >= 1 {
		wait = time.Duration(timeout[0]) * time.Second
	}

	conn, err := net.DialTimeout("tcp", sock.server, wait)
	sock.conn = conn

	errcheck(err) //ADD: RECONNECT
	debug("Connect:", sock.server, "\n")
}

// Receive receives new messages from the server and forwards them to parse().
func (sock *Server) Receive() {

	go func() {
		debug("Receive:", sock.server, "\n")
		sock.sendroutine() //Non-Blocking
		reader := bufio.NewReader(sock.conn)

		for {
			line, err := reader.ReadString('\n')
			errcheck(err)
			debug("<-", line)
			sock.parse(strings.Trim(line, "\r\n")) //Remove \r\n for easier parsing
		}

	}()

	time.Sleep(time.Second)
	sock.Nick(sock.nickname)
	sock.user(sock.username, "0", "0", sock.realname)
}

// parse parses incoming messages from the server and triggers predefined events.
func (sock *Server) parse(line string) {
	split := strings.SplitN(line, " ", 4)
	split = append(split, make([]string, 4-len(split), 4-len(split))...)

	switch true {
	case split[0] == "PING":
		sock.pong(split[1]) //Ping e.g.: PING :B97B6379
	case split[1] == "PRIVMSG" && split[3][1:] == "!ping":
		sock.Privmsg(split[2], "!pong")
	case split[1] == "376":
		sock.Join("#irccs")
	}
}

// Nick sets or changes the nickname. The IRC server might reply an errorcode,
// if the requsted nickname is not valid or in use.
func (sock *Server) Nick(nickname string) {
	sock.nickname = nickname
	sock.Send("NICK " + nickname)
}

// user specifies the userdata at the beginning of a new connection.
// Servername and hostname are likely to be ignored by the IRC server.
// Scheme: (Nickname!Username@Hostname): Real Name
func (sock *Server) user(username, hostname, servername, realname string) {
	sock.Send("USER " + username + " " + hostname + " " + servername + " :" + realname)
}

// Join joins the specified channel(s). Multiple channels need to be
// seperated by a colon. */
func (sock *Server) Join(channel string) {
	sock.Send("JOIN " + channel)
}

// pong answers a ping request with a previously received reply string.
func (sock *Server) pong(reply string) {
	sock.Send("PONG " + reply)
}

// Privmsg sends a private message to a user or a channel.
// The parameter user can also contain multiple receivers seperated by a colon.
func (sock *Server) Privmsg(user string, message string) {
	sock.Send("PRIVMSG " + user + " :" + message)
}

// Send adds the message to the RAW Message queue
func (sock *Server) Send(message string) {
	go func() {
		sock.sendqueue <- message + "\n"
	}()
}

// sendroutine processes the sendqueue and sends the messages to the server.
// sendrouting must be executed as goroutine!
func (sock *Server) sendroutine() {
	go func() {
		for {
			smsg := <-sock.sendqueue
			//fmt.Fprint(sock.conn, smsg)
			io.WriteString(sock.conn, smsg)
			debug("->", smsg)
		}
	}()
}

// DEBUG FUNCTIONS

func errcheck(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func debug(s ...interface{}) {
	fmt.Print(s...) //log.Print adds timestamp
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package ddl

import (
	"fmt"
	"time"

	. "github.com/pingcap/check"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/meta"
	"github.com/pingcap/tidb/store/localstore"
	"github.com/pingcap/tidb/store/localstore/goleveldb"
	"github.com/pingcap/tidb/terror"
)

var _ = Suite(&testDDLSuite{})

func testCreateStore(c *C, name string) kv.Storage {
	driver := localstore.Driver{Driver: goleveldb.MemoryDriver{}}
	store, err := driver.Open(fmt.Sprintf("memory:%s", name))
	c.Assert(err, IsNil)
	return store
}

type testDDLSuite struct {
}

func testCheckOwner(c *C, d *ddl, isOwner bool) {
	err := kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error {
		t := meta.NewMeta(txn)
		_, err := d.checkOwner(t)
		return err
	})
	if isOwner {
		c.Assert(err, IsNil)
		return
	}

	c.Assert(terror.ErrorEqual(err, ErrNotOwner), IsTrue)
}

func (s *testDDLSuite) TestCheckOnwer(c *C) {
	store := testCreateStore(c, "test_owner")
	defer store.Close()

	lease := 100 * time.Millisecond
	d1 := newDDL(store, nil, nil, lease)
	defer d1.close()

	time.Sleep(lease)

	testCheckOwner(c, d1, true)

	d2 := newDDL(store, nil, nil, lease)
	defer d2.close()

	testCheckOwner(c, d2, false)
	d1.close()

	time.Sleep(6 * lease)

	testCheckOwner(c, d2, true)

	d2.SetLease(1 * time.Second)
}
<commit_msg>ddl: cover error test.<commit_after>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package ddl

import (
	"fmt"
	"time"

	. "github.com/pingcap/check"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/meta"
	"github.com/pingcap/tidb/model"
	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/parser/coldef"
	"github.com/pingcap/tidb/store/localstore"
	"github.com/pingcap/tidb/store/localstore/goleveldb"
	"github.com/pingcap/tidb/terror"
	"github.com/pingcap/tidb/util/types"
)

var _ = Suite(&testDDLSuite{})

func testCreateStore(c *C, name string) kv.Storage {
	driver := localstore.Driver{Driver: goleveldb.MemoryDriver{}}
	store, err := driver.Open(fmt.Sprintf("memory:%s", name))
	c.Assert(err, IsNil)
	return store
}

type testDDLSuite struct {
}

func testCheckOwner(c *C, d *ddl, isOwner bool) {
	err := kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error {
		t := meta.NewMeta(txn)
		_, err := d.checkOwner(t)
		return err
	})
	if isOwner {
		c.Assert(err, IsNil)
		return
	}

	c.Assert(terror.ErrorEqual(err, ErrNotOwner), IsTrue)
}

func (s *testDDLSuite) TestCheckOnwer(c *C) {
	store := testCreateStore(c, "test_owner")
	defer store.Close()

	lease := 100 * time.Millisecond
	d1 := newDDL(store, nil, nil, lease)
	defer d1.close()

	time.Sleep(lease)

	testCheckOwner(c, d1, true)

	d2 := newDDL(store, nil, nil, lease)
	defer d2.close()

	testCheckOwner(c, d2, false)
	d1.close()

	time.Sleep(6 * lease)

	testCheckOwner(c, d2, true)

	d2.SetLease(1 * time.Second)
}

func (s *testDDLSuite) TestSchemaError(c *C) {
	store := testCreateStore(c, "test_schema_error")
	defer store.Close()

	lease := 50 * time.Millisecond

	d := newDDL(store, nil, nil, lease)
	defer d.close()

	job := &model.Job{
		SchemaID: 1,
		Type:     model.ActionCreateSchema,
		Args:     []interface{}{1},
	}

	ctx := testNewContext(c, d)

	err := d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

}

func (s *testDDLSuite) TestTableError(c *C) {
	store := testCreateStore(c, "test_table_error")
	defer store.Close()

	lease := 50 * time.Millisecond

	d := newDDL(store, nil, nil, lease)
	defer d.close()

	job := &model.Job{
		SchemaID: 1,
		TableID:  1,
		Type:     model.ActionCreateTable,
		Args:     []interface{}{1},
	}

	ctx := testNewContext(c, d)

	err := d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job.SchemaID = -1
	job.State = 0
	tblInfo := testTableInfo(c, d, "t", 3)
	job.Args = []interface{}{tblInfo}

	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job = &model.Job{
		SchemaID: 1,
		TableID:  1,
		Type:     model.ActionDropTable,
	}

	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	dbInfo := testSchemaInfo(c, d, "test")
	testCreateSchema(c, testNewContext(c, d), d, dbInfo)

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  -1,
		Type:     model.ActionDropTable,
	}

	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	testCreateTable(c, ctx, d, dbInfo, tblInfo)

	err = kv.RunInNewTxn(store, false, func(txn kv.Transaction) error {
		job.SchemaID = -1
		job.TableID = -1
		t := meta.NewMeta(txn)
		_, err1 := d.getTableInfo(t, job)
		c.Assert(err1, NotNil)
		job.SchemaID = dbInfo.ID
		_, err1 = d.getTableInfo(t, job)
		c.Assert(err1, NotNil)
		return nil
	})
	c.Assert(err, IsNil)
}

func (s *testDDLSuite) TestIndexError(c *C) {
	store := testCreateStore(c, "test_index_error")
	defer store.Close()

	lease := 50 * time.Millisecond

	d := newDDL(store, nil, nil, lease)
	defer d.close()

	ctx := testNewContext(c, d)

	job := &model.Job{
		SchemaID: -1,
		TableID:  1,
		Type:     model.ActionAddIndex,
	}

	err := d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job = &model.Job{
		SchemaID: -1,
		TableID:  1,
		Type:     model.ActionDropIndex,
	}

	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	dbInfo := testSchemaInfo(c, d, "test")
	tblInfo := testTableInfo(c, d, "t", 3)

	testCreateSchema(c, ctx, d, dbInfo)
	testCreateTable(c, ctx, d, dbInfo, tblInfo)

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  tblInfo.ID,
		Type:     model.ActionAddIndex,
		Args:     []interface{}{1},
	}
	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  tblInfo.ID,
		Type:     model.ActionAddIndex,
		Args:     []interface{}{false, model.NewCIStr("t"), []*coldef.IndexColName{{ColumnName: "c", Length: 256}}},
	}
	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  tblInfo.ID,
		Type:     model.ActionAddIndex,
		Args:     []interface{}{false, model.NewCIStr("c1_index"), []*coldef.IndexColName{{ColumnName: "c", Length: 256}}},
	}
	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	testCreateIndex(c, ctx, d, dbInfo, tblInfo, false, "c1_index", "c1")

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  tblInfo.ID,
		Type:     model.ActionAddIndex,
		Args:     []interface{}{false, model.NewCIStr("c1_index"), []*coldef.IndexColName{{ColumnName: "c1", Length: 256}}},
	}
	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  tblInfo.ID,
		Type:     model.ActionDropIndex,
		Args:     []interface{}{1},
	}
	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	testDropIndex(c, ctx, d, dbInfo, tblInfo, "c1_index")

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  tblInfo.ID,
		Type:     model.ActionDropIndex,
		Args:     []interface{}{model.NewCIStr("c1_index")},
	}
	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)
}

func (s *testDDLSuite) TestColumnError(c *C) {
	store := testCreateStore(c, "test_column_error")
	defer store.Close()

	lease := 50 * time.Millisecond

	d := newDDL(store, nil, nil, lease)
	defer d.close()

	ctx := testNewContext(c, d)

	job := &model.Job{
		SchemaID: -1,
		TableID:  1,
		Type:     model.ActionAddColumn,
	}

	err := d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job = &model.Job{
		SchemaID: -1,
		TableID:  1,
		Type:     model.ActionDropColumn,
	}

	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	dbInfo := testSchemaInfo(c, d, "test")
	tblInfo := testTableInfo(c, d, "t", 3)

	testCreateSchema(c, ctx, d, dbInfo)
	testCreateTable(c, ctx, d, dbInfo, tblInfo)

	col := &model.ColumnInfo{
		Name:         model.NewCIStr("c4"),
		Offset:       len(tblInfo.Columns),
		DefaultValue: 0,
	}

	col.ID, err = d.genGlobalID()
	c.Assert(err, IsNil)

	col.FieldType = *types.NewFieldType(mysql.TypeLong)
	pos := &ColumnPosition{Type: ColumnPositionAfter, RelativeColumn: "c5"}

	job = &model.Job{
		SchemaID: dbInfo.ID,
		TableID:  tblInfo.ID,
		Type:     model.ActionAddColumn,
		Args:     []interface{}{col, pos, 0},
	}

	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)

	job = &model.Job{
		SchemaID: -1,
		TableID:  1,
		Type:     model.ActionDropColumn,
		Args:     []interface{}{1},
	}

	err = d.startJob(ctx, job)
	c.Assert(err, NotNil)
	testCheckJobCancelled(c, d, job)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type memoryStorage struct {
	Settings settings
}

func (s *memoryStorage) getSettings() settings {
	fmt.Printf("Getting settings: %v\n", s.Settings)
	return s.Settings
}

func (s *memoryStorage) saveSettings(sets settings) {
	fmt.Printf("Saving settings: %d, %s\n", sets.YahooCityId, sets.YahooTempUnit)
	s.Settings.YahooCityId = sets.YahooCityId
	s.Settings.YahooTempUnit = sets.YahooTempUnit
	fmt.Printf("Settings saved. %v\n", s.Settings)
}
<commit_msg>Improve memory storage.<commit_after>package main

import (
	"fmt"
)

type memoryStorage struct {
	Settings      settings
	ScheduleItems []scheduleItem
}

func (s *memoryStorage) getSettings() settings {
	fmt.Printf("Getting settings: %v\n", s.Settings)
	return s.Settings
}

func (s *memoryStorage) saveSettings(sets settings) {
	fmt.Printf("Saving settings: %d, %s\n", sets.YahooCityId, sets.YahooTempUnit)
	s.Settings.YahooCityId = sets.YahooCityId
	s.Settings.YahooTempUnit = sets.YahooTempUnit
	fmt.Printf("Settings saved. %v\n", s.Settings)
}

func (s *memoryStorage) getSchedules() []scheduleItem {
	return s.ScheduleItems
}

func (s *memoryStorage) saveSchedules(items []scheduleItem) {
	s.ScheduleItems = items
}
<|endoftext|>


<commit_before>package menu

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"

	"github.com/fatih/color"
	"github.com/miquella/ask"
	"golang.org/x/crypto/ssh"

	"github.com/miquella/vaulted/lib"
)

type SSHKeyMenu struct {
	*Menu
}

func (m *SSHKeyMenu) Help() {
	menuColor.Set()
	defer color.Unset()

	fmt.Println("a,add      - Add")
	fmt.Println("D,delete   - Delete")
	fmt.Println("g,generate - Generate Key")
	fmt.Println("v          - HashiCorp Vault Signing URL")
	fmt.Println("u,users    - HashiCorp Vault User Principals")
	fmt.Println("E          - Expose External SSH Agent")
	fmt.Println("?,help     - Help")
	fmt.Println("b,back     - Back")
	fmt.Println("q,quit     - Quit")
}

func (m *SSHKeyMenu) Handler() error {
	for {
		var err error
		m.Printer()
		input, err := interaction.ReadMenu("Edit ssh keys: [a,D,g,v,u,E,b]: ")
		if err != nil {
			return err
		}
		switch input {
		case "a", "add", "key", "keys":
			err = m.AddSSHKey()
		case "D", "delete", "remove":
			var key string
			key, err = interaction.ReadValue("Key: ")
			if err == nil {
				if _, exists := m.Vault.SSHKeys[key]; exists {
					delete(m.Vault.SSHKeys, key)
				} else {
					color.Red("Key '%s' not found", key)
				}
			}
		case "g", "generate":
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.GenerateRSAKey = !m.Vault.SSHOptions.GenerateRSAKey
		case "v":
			signingUrl, err := interaction.ReadValue("HashiCorp Vault signing URL: ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.VaultSigningUrl = signingUrl

			if signingUrl != "" && !m.Vault.SSHOptions.GenerateRSAKey {
				generateKey, _ := interaction.ReadValue("Would you like to enable RSA key generation (y/n): ")
				if generateKey == "y" {
					m.Vault.SSHOptions.GenerateRSAKey = true
				}
			}
		case "u", "users":
			userPrincipals, err := interaction.ReadValue("HashiCorp Vault user principals (comma separated): ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			if userPrincipals != "" {
				m.Vault.SSHOptions.ValidPrincipals = strings.Split(userPrincipals, ",")
			} else {
				m.Vault.SSHOptions.ValidPrincipals = []string{}
			}
		case "E":
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.DisableProxy = !m.Vault.SSHOptions.DisableProxy
		case "b", "back":
			return nil
		case "q", "quit", "exit":
			var confirm string
			confirm, err = interaction.ReadValue("Are you sure you wish to save and exit the vault? (y/n): ")
			if err == nil {
				if confirm == "y" {
					return ErrSaveAndExit
				}
			}
		case "?", "help":
			m.Help()
		default:
			color.Red("Command not recognized")
		}

		if err != nil {
			return err
		}
	}
}

func (m *SSHKeyMenu) AddSSHKey() error {
	var err error

	homeDir := ""
	user, err := user.Current()
	if err == nil {
		homeDir = user.HomeDir
	} else {
		homeDir = os.Getenv("HOME")
	}

	defaultFilename := ""
	filename := ""
	if homeDir != "" {
		defaultFilename = filepath.Join(homeDir, ".ssh", "id_rsa")
		filename, err = interaction.ReadValue(fmt.Sprintf("Key file (default: %s): ", defaultFilename))
		if err != nil {
			return err
		}
		if filename == "" {
			filename = defaultFilename
		}
		if !filepath.IsAbs(filename) {
			filename = filepath.Join(filepath.Join(homeDir, ".ssh"), filename)
		}
	} else {
		filename, err = interaction.ReadValue("Key file: ")
		if err != nil {
			return err
		}
	}

	decryptedBlock, err := loadAndDecryptKey(filename)
	if err != nil {
		color.Red("%v", err)
		return nil
	}

	comment := loadPublicKeyComment(filename + ".pub")
	var name string
	if comment != "" {
		name, err = interaction.ReadValue(fmt.Sprintf("Name (default: %s): ", comment))
		if err != nil {
			return err
		}
		if name == "" {
			name = comment
		}
	} else {
		name, err = interaction.ReadValue("Name: ")
		if err != nil {
			return err
		}
		if name == "" {
			name = filename
		}
	}

	if m.Vault.SSHKeys == nil {
		m.Vault.SSHKeys = make(map[string]string)
	}
	m.Vault.SSHKeys[name] = string(pem.EncodeToMemory(decryptedBlock))

	return nil
}

func loadAndDecryptKey(filename string) (*pem.Block, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(data)
	if block == nil {
		return nil, err
	}

	if x509.IsEncryptedPEMBlock(block) {
		var passphrase string
		var decryptedBytes []byte
		for i := 0; i < 3; i++ {
			passphrase, err = ask.HiddenAsk("Passphrase: ")
			if err != nil {
				return nil, err
			}

			decryptedBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
			if err == nil {
				break
			}
			if err != x509.IncorrectPasswordError {
				return nil, err
			}
		}

		if err != nil {
			return nil, err
		}

		return &pem.Block{
			Type:  block.Type,
			Bytes: decryptedBytes,
		}, nil
	}
	return block, nil
}

func loadPublicKeyComment(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		return ""
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return ""
	}

	_, comment, _, _, err := ssh.ParseAuthorizedKey(data)
	if err != nil {
		return ""
	}
	return comment
}

func (m *SSHKeyMenu) Printer() {
	color.Cyan("\nSSH Agent:")
	color.Cyan("  Keys:")
	if len(m.Vault.SSHKeys) > 0 || m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
		keys := []string{}
		for key := range m.Vault.SSHKeys {
			keys = append(keys, key)
		}
		sort.Strings(keys)

		for _, key := range keys {
			green.Printf("    %s\n", key)
		}

		if m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
			faintColor.Print("    <generated RSA key>\n")
		}
	} else {
		fmt.Println("    [Empty]")
	}

	if m.Vault.SSHOptions != nil {
		if m.Vault.SSHOptions.VaultSigningUrl != "" || len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
			color.Cyan("\n  Signing (HashiCorp Vault):")
			if m.Vault.SSHOptions.VaultSigningUrl != "" {
				green.Printf("    URL: ")
				fmt.Printf("%s\n", m.Vault.SSHOptions.VaultSigningUrl)
			}

			if len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
				green.Printf("    User(s): ")
				fmt.Printf("%s\n", strings.Join(m.Vault.SSHOptions.ValidPrincipals, ", "))
			}

			cyan.Print("\n  Expose external SSH agent: ")
			fmt.Printf("%t\n", !m.Vault.SSHOptions.DisableProxy)
		}
	}
}
<commit_msg>fix edit display of proxy without signing enabled<commit_after>package menu

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"

	"github.com/fatih/color"
	"github.com/miquella/ask"
	"golang.org/x/crypto/ssh"

	"github.com/miquella/vaulted/lib"
)

type SSHKeyMenu struct {
	*Menu
}

func (m *SSHKeyMenu) Help() {
	menuColor.Set()
	defer color.Unset()

	fmt.Println("a,add      - Add")
	fmt.Println("D,delete   - Delete")
	fmt.Println("g,generate - Generate Key")
	fmt.Println("v          - HashiCorp Vault Signing URL")
	fmt.Println("u,users    - HashiCorp Vault User Principals")
	fmt.Println("E          - Expose External SSH Agent")
	fmt.Println("?,help     - Help")
	fmt.Println("b,back     - Back")
	fmt.Println("q,quit     - Quit")
}

func (m *SSHKeyMenu) Handler() error {
	for {
		var err error
		m.Printer()
		input, err := interaction.ReadMenu("Edit ssh keys: [a,D,g,v,u,E,b]: ")
		if err != nil {
			return err
		}
		switch input {
		case "a", "add", "key", "keys":
			err = m.AddSSHKey()
		case "D", "delete", "remove":
			var key string
			key, err = interaction.ReadValue("Key: ")
			if err == nil {
				if _, exists := m.Vault.SSHKeys[key]; exists {
					delete(m.Vault.SSHKeys, key)
				} else {
					color.Red("Key '%s' not found", key)
				}
			}
		case "g", "generate":
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.GenerateRSAKey = !m.Vault.SSHOptions.GenerateRSAKey
		case "v":
			signingUrl, err := interaction.ReadValue("HashiCorp Vault signing URL: ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.VaultSigningUrl = signingUrl

			if signingUrl != "" && !m.Vault.SSHOptions.GenerateRSAKey {
				generateKey, _ := interaction.ReadValue("Would you like to enable RSA key generation (y/n): ")
				if generateKey == "y" {
					m.Vault.SSHOptions.GenerateRSAKey = true
				}
			}
		case "u", "users":
			userPrincipals, err := interaction.ReadValue("HashiCorp Vault user principals (comma separated): ")
			if err != nil {
				return err
			}
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			if userPrincipals != "" {
				m.Vault.SSHOptions.ValidPrincipals = strings.Split(userPrincipals, ",")
			} else {
				m.Vault.SSHOptions.ValidPrincipals = []string{}
			}
		case "E":
			if m.Vault.SSHOptions == nil {
				m.Vault.SSHOptions = &vaulted.SSHOptions{}
			}
			m.Vault.SSHOptions.DisableProxy = !m.Vault.SSHOptions.DisableProxy
		case "b", "back":
			return nil
		case "q", "quit", "exit":
			var confirm string
			confirm, err = interaction.ReadValue("Are you sure you wish to save and exit the vault? (y/n): ")
			if err == nil {
				if confirm == "y" {
					return ErrSaveAndExit
				}
			}
		case "?", "help":
			m.Help()
		default:
			color.Red("Command not recognized")
		}

		if err != nil {
			return err
		}
	}
}

func (m *SSHKeyMenu) AddSSHKey() error {
	var err error

	homeDir := ""
	user, err := user.Current()
	if err == nil {
		homeDir = user.HomeDir
	} else {
		homeDir = os.Getenv("HOME")
	}

	defaultFilename := ""
	filename := ""
	if homeDir != "" {
		defaultFilename = filepath.Join(homeDir, ".ssh", "id_rsa")
		filename, err = interaction.ReadValue(fmt.Sprintf("Key file (default: %s): ", defaultFilename))
		if err != nil {
			return err
		}
		if filename == "" {
			filename = defaultFilename
		}
		if !filepath.IsAbs(filename) {
			filename = filepath.Join(filepath.Join(homeDir, ".ssh"), filename)
		}
	} else {
		filename, err = interaction.ReadValue("Key file: ")
		if err != nil {
			return err
		}
	}

	decryptedBlock, err := loadAndDecryptKey(filename)
	if err != nil {
		color.Red("%v", err)
		return nil
	}

	comment := loadPublicKeyComment(filename + ".pub")
	var name string
	if comment != "" {
		name, err = interaction.ReadValue(fmt.Sprintf("Name (default: %s): ", comment))
		if err != nil {
			return err
		}
		if name == "" {
			name = comment
		}
	} else {
		name, err = interaction.ReadValue("Name: ")
		if err != nil {
			return err
		}
		if name == "" {
			name = filename
		}
	}

	if m.Vault.SSHKeys == nil {
		m.Vault.SSHKeys = make(map[string]string)
	}
	m.Vault.SSHKeys[name] = string(pem.EncodeToMemory(decryptedBlock))

	return nil
}

func loadAndDecryptKey(filename string) (*pem.Block, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(data)
	if block == nil {
		return nil, err
	}

	if x509.IsEncryptedPEMBlock(block) {
		var passphrase string
		var decryptedBytes []byte
		for i := 0; i < 3; i++ {
			passphrase, err = ask.HiddenAsk("Passphrase: ")
			if err != nil {
				return nil, err
			}

			decryptedBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
			if err == nil {
				break
			}
			if err != x509.IncorrectPasswordError {
				return nil, err
			}
		}

		if err != nil {
			return nil, err
		}

		return &pem.Block{
			Type:  block.Type,
			Bytes: decryptedBytes,
		}, nil
	}
	return block, nil
}

func loadPublicKeyComment(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		return ""
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return ""
	}

	_, comment, _, _, err := ssh.ParseAuthorizedKey(data)
	if err != nil {
		return ""
	}
	return comment
}

func (m *SSHKeyMenu) Printer() {
	color.Cyan("\nSSH Agent:")
	color.Cyan("  Keys:")
	if len(m.Vault.SSHKeys) > 0 || m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
		keys := []string{}
		for key := range m.Vault.SSHKeys {
			keys = append(keys, key)
		}
		sort.Strings(keys)

		for _, key := range keys {
			green.Printf("    %s\n", key)
		}

		if m.Vault.SSHOptions != nil && m.Vault.SSHOptions.GenerateRSAKey {
			faintColor.Print("    <generated RSA key>\n")
		}
	} else {
		fmt.Println("    [Empty]")
	}

	if m.Vault.SSHOptions != nil {
		if m.Vault.SSHOptions.VaultSigningUrl != "" || len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
			color.Cyan("\n  Signing (HashiCorp Vault):")
			if m.Vault.SSHOptions.VaultSigningUrl != "" {
				green.Printf("    URL: ")
				fmt.Printf("%s\n", m.Vault.SSHOptions.VaultSigningUrl)
			}

			if len(m.Vault.SSHOptions.ValidPrincipals) > 0 {
				green.Printf("    User(s): ")
				fmt.Printf("%s\n", strings.Join(m.Vault.SSHOptions.ValidPrincipals, ", "))
			}
		}

		cyan.Print("\n  Expose external SSH agent: ")
		fmt.Printf("%t\n", !m.Vault.SSHOptions.DisableProxy)
	}
}
<|endoftext|>


<commit_before>
package main

import (
	"fmt"
	"math"
)

type ErrNegativeSqrt float64

func Sqrt(x float64) (float64, error) {

	if x < 0 {
		return 0, ErrNegativeSqrt(x)
	}

	if x == 0 {
		return 0, nil
	}

	z := 1.0
	prev := 0.0
	const delta = 0.00000000001

	for math.Abs(z-prev) > delta {
		prev = z
		z -= (z*z-x)/(2*z)
	}

	return z, nil
}

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

func main() {
	for i := -5; i < 10; i++ {
		res, err := Sqrt(float64(i))
		if err != nil {
			fmt.Println("Sqrt error:", err)
		} else {
			fmt.Printf("Sqrt(%v) = %v\n", i, res)
		}
	}
}
<commit_msg>Added "Errors" exercise description from the Go Tour<commit_after>/* Taken from the Go Tour (Exercise: Errors)
 *
 * Copy your Sqrt function from the earlier exercise and modify it to return an error value.
 *
 * Sqrt should return a non-nil error value when given a negative number, as it doesn't support
 * complex numbers.
 *
 * Create a new type
 *
 * type ErrNegativeSqrt float64
 *
 * and make it an error by giving it a
 *
 * func (e ErrNegativeSqrt) Error() string
 *
 * method such that ErrNegativeSqrt(-2).Error() returns "cannot Sqrt negative number: -2".
 *
 * Note: a call to fmt.Sprint(e) inside the Error method will send the program into an infinite
 * loop. You can avoid this by converting e first: fmt.Sprint(float64(e)). Why?
 *
 * Change your Sqrt function to return an ErrNegativeSqrt value when given a negative number.
 *
 */

package main

import (
	"fmt"
	"math"
)

type ErrNegativeSqrt float64

func Sqrt(x float64) (float64, error) {

	if x < 0 {
		return 0, ErrNegativeSqrt(x)
	}

	if x == 0 {
		return 0, nil
	}

	z := 1.0
	prev := 0.0
	const delta = 0.00000000001

	for math.Abs(z-prev) > delta {
		prev = z
		z -= (z*z-x)/(2*z)
	}

	return z, nil
}

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

func main() {
	for i := -5; i < 10; i++ {
		res, err := Sqrt(float64(i))
		if err != nil {
			fmt.Println("Sqrt error:", err)
		} else {
			fmt.Printf("Sqrt(%v) = %v\n", i, res)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 gcp abstracts interacting with certain aspects of Google Cloud
// Platform, such as creating service account keys and access tokens.
package gcp

import (
	"context"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"net/http"
	"path"

	"golang.org/x/crypto/sha3"
	cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
	"google.golang.org/api/googleapi"
	iam "google.golang.org/api/iam/v1"
	iamcredentials "google.golang.org/api/iamcredentials/v1"
)

// AccountWarehouseOptions is used with NewWarehouse to configure a new warehouse.
type AccountWarehouseOptions struct {
	Project     string
	DefaultRole string
	Scopes      []string
}

// AccountWarehouse is used to create Google Cloud Platform Service Account
// keys and access tokens associated with a specific identity.
type AccountWarehouse struct {
	opts  AccountWarehouseOptions
	iam   *iam.Service
	creds *iamcredentials.Service
	crm   *cloudresourcemanager.Service
}

// NewAccountWarehouse creates a new AccountWarehouse using the provided client
// and options.
func NewAccountWarehouse(client *http.Client, opts *AccountWarehouseOptions) (*AccountWarehouse, error) {
	iamSvc, err := iam.New(client)
	if err != nil {
		return nil, fmt.Errorf("creating IAM client: %v", err)
	}

	creds, err := iamcredentials.New(client)
	if err != nil {
		return nil, fmt.Errorf("creating IAM credentials client: %v", err)
	}

	crm, err := cloudresourcemanager.New(client)
	if err != nil {
		return nil, fmt.Errorf("creating cloud resource manager client: %v", err)
	}

	return &AccountWarehouse{
		opts:  *opts,
		iam:   iamSvc,
		creds: creds,
		crm:   crm,
	}, nil
}

// GetAccountKey returns a service account key associated with id.
func (wh *AccountWarehouse) GetAccountKey(ctx context.Context, id string) ([]byte, error) {
	account, err := wh.getBackingAccount(ctx, id)
	if err != nil {
		return nil, fmt.Errorf("getting backing account: %v", err)
	}

	// TODO(#2): Remove old keys from service account before creating new keys.
	keys := wh.iam.Projects.ServiceAccounts.Keys
	result, err := keys.Create(accountID("-", account), &iam.CreateServiceAccountKeyRequest{
		PrivateKeyType: "TYPE_GOOGLE_CREDENTIALS_FILE",
	}).Context(ctx).Do()
	if err != nil {
		return nil, fmt.Errorf("creating key: %v", err)
	}

	out, err := base64.StdEncoding.DecodeString(result.PrivateKeyData)
	if err != nil {
		return nil, fmt.Errorf("decoding key: %v", err)
	}

	return out, nil
}

// GetAccessToken returns an access token for the service account uniquely
// associated with id.
func (wh *AccountWarehouse) GetAccessToken(ctx context.Context, id string) (string, error) {
	account, err := wh.getBackingAccount(ctx, id)
	if err != nil {
		return "", fmt.Errorf("getting backing account: %v", err)
	}

	response, err := wh.creds.Projects.ServiceAccounts.GenerateAccessToken(accountID("-", account), &iamcredentials.GenerateAccessTokenRequest{
		Scope: wh.opts.Scopes,
	}).Context(ctx).Do()
	if err != nil {
		return "", fmt.Errorf("generating access token: %v", err)
	}

	return response.AccessToken, nil
}

func (wh *AccountWarehouse) getBackingAccount(ctx context.Context, id string) (string, error) {
	service := wh.iam.Projects.ServiceAccounts

	hid := hashID(id)
	name := accountID(wh.opts.Project, fmt.Sprintf("%s@%s.iam.gserviceaccount.com", hid, wh.opts.Project))
	account, err := service.Get(name).Context(ctx).Do()
	if err == nil {
		return account.Email, nil
	}
	if err, ok := err.(*googleapi.Error); !ok || err.Code != http.StatusNotFound {
		return "", fmt.Errorf("getting account: %v", err)
	}

	account, err = service.Create(projectID(wh.opts.Project), &iam.CreateServiceAccountRequest{
		AccountId: hid,
		ServiceAccount: &iam.ServiceAccount{
			DisplayName: id,
		},
	}).Context(ctx).Do()
	if err != nil {
		return "", fmt.Errorf("creating backing account: %v", err)
	}
	// TODO(#3): Update IAM policy bindings to add SA to default role.
	return account.Email, nil
}

func hashID(id string) string {
	hash := sha3.Sum224([]byte(id))
	return "i" + hex.EncodeToString(hash[:])[:29]
}

func keyID(project, account, key string) string {
	return path.Join(accountID(project, account), "keys", key)
}

func accountID(project, account string) string {
	return path.Join(projectID(project), "serviceAccounts", account)
}

func projectID(project string) string {
	return path.Join("projects", project)
}
<commit_msg>gcp: Make AccountWarehouse assign roles (#3)<commit_after>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 gcp abstracts interacting with certain aspects of Google Cloud
// Platform, such as creating service account keys and access tokens.
package gcp

import (
	"context"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"net/http"
	"path"
	"strings"

	"golang.org/x/crypto/sha3"
	cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
	"google.golang.org/api/googleapi"
	iam "google.golang.org/api/iam/v1"
	iamcredentials "google.golang.org/api/iamcredentials/v1"
)

// AccountWarehouseOptions is used with NewWarehouse to configure a new warehouse.
type AccountWarehouseOptions struct {
	Project     string
	DefaultRole string
	Scopes      []string
}

// AccountWarehouse is used to create Google Cloud Platform Service Account
// keys and access tokens associated with a specific identity.
type AccountWarehouse struct {
	opts  AccountWarehouseOptions
	iam   *iam.Service
	creds *iamcredentials.Service
	crm   *cloudresourcemanager.Service
}

// NewAccountWarehouse creates a new AccountWarehouse using the provided client
// and options.
func NewAccountWarehouse(client *http.Client, opts *AccountWarehouseOptions) (*AccountWarehouse, error) {
	iamSvc, err := iam.New(client)
	if err != nil {
		return nil, fmt.Errorf("creating IAM client: %v", err)
	}

	creds, err := iamcredentials.New(client)
	if err != nil {
		return nil, fmt.Errorf("creating IAM credentials client: %v", err)
	}

	crm, err := cloudresourcemanager.New(client)
	if err != nil {
		return nil, fmt.Errorf("creating cloud resource manager client: %v", err)
	}

	return &AccountWarehouse{
		opts:  *opts,
		iam:   iamSvc,
		creds: creds,
		crm:   crm,
	}, nil
}

// GetAccountKey returns a service account key associated with id.
func (wh *AccountWarehouse) GetAccountKey(ctx context.Context, id string) ([]byte, error) {
	account, err := wh.getBackingAccount(ctx, id)
	if err != nil {
		return nil, fmt.Errorf("getting backing account: %v", err)
	}

	// TODO(#2): Remove old keys from service account before creating new keys.
	keys := wh.iam.Projects.ServiceAccounts.Keys
	result, err := keys.Create(accountID("-", account), &iam.CreateServiceAccountKeyRequest{
		PrivateKeyType: "TYPE_GOOGLE_CREDENTIALS_FILE",
	}).Context(ctx).Do()
	if err != nil {
		return nil, fmt.Errorf("creating key: %v", err)
	}

	out, err := base64.StdEncoding.DecodeString(result.PrivateKeyData)
	if err != nil {
		return nil, fmt.Errorf("decoding key: %v", err)
	}

	return out, nil
}

// GetAccessToken returns an access token for the service account uniquely
// associated with id.
func (wh *AccountWarehouse) GetAccessToken(ctx context.Context, id string) (string, error) {
	account, err := wh.getBackingAccount(ctx, id)
	if err != nil {
		return "", fmt.Errorf("getting backing account: %v", err)
	}

	response, err := wh.creds.Projects.ServiceAccounts.GenerateAccessToken(accountID("-", account), &iamcredentials.GenerateAccessTokenRequest{
		Scope: wh.opts.Scopes,
	}).Context(ctx).Do()
	if err != nil {
		return "", fmt.Errorf("generating access token: %v", err)
	}

	return response.AccessToken, nil
}

func (wh *AccountWarehouse) getBackingAccount(ctx context.Context, id string) (string, error) {
	service := wh.iam.Projects.ServiceAccounts

	hid := hashID(id)
	name := accountID(wh.opts.Project, fmt.Sprintf("%s@%s.iam.gserviceaccount.com", hid, wh.opts.Project))
	account, err := service.Get(name).Context(ctx).Do()
	if err == nil {
		if err := wh.configureRole(ctx, account.Email); err != nil {
			return "", fmt.Errorf("configuring role for existing account: %v", err)
		}
		return account.Email, nil
	}
	if err, ok := err.(*googleapi.Error); !ok || err.Code != http.StatusNotFound {
		return "", fmt.Errorf("getting account: %v", err)
	}

	account, err = service.Create(projectID(wh.opts.Project), &iam.CreateServiceAccountRequest{
		AccountId: hid,
		ServiceAccount: &iam.ServiceAccount{
			DisplayName: id,
		},
	}).Context(ctx).Do()
	if err != nil {
		return "", fmt.Errorf("creating backing account: %v", err)
	}
	if err := wh.configureRole(ctx, account.Email); err != nil {
		return "", fmt.Errorf("configuring role for new account: %v", err)
	}
	return account.Email, nil
}

func (wh *AccountWarehouse) configureRole(ctx context.Context, email string) error {
	role := wh.opts.DefaultRole

	project := wh.opts.Project
	if parts := strings.Split(role, "/"); len(parts) == 4 && parts[0] == "projects" {
		project = parts[1]
	}

	projects := wh.crm.Projects
	policy, err := projects.GetIamPolicy(project, &cloudresourcemanager.GetIamPolicyRequest{}).Context(ctx).Do()
	if err != nil {
		return fmt.Errorf("getting IAM policy for project %q: %v", project, err)
	}

	var binding *cloudresourcemanager.Binding
	for _, b := range policy.Bindings {
		if b.Role == role {
			binding = b
			break
		}
	}
	if binding == nil {
		return fmt.Errorf("no bindings for %q in policy", role)
	}

	qualifiedName := "serviceAccount:" + email
	for _, member := range binding.Members {
		if member == qualifiedName {
			return nil
		}
	}

	binding.Members = append(binding.Members, qualifiedName)
	_, err = projects.SetIamPolicy(project, &cloudresourcemanager.SetIamPolicyRequest{Policy: policy}).Context(ctx).Do()
	if err != nil {
		return fmt.Errorf("setting IAM policy for project %q: %v", project, err)
	}
	return nil
}

func hashID(id string) string {
	hash := sha3.Sum224([]byte(id))
	return "i" + hex.EncodeToString(hash[:])[:29]
}

func keyID(project, account, key string) string {
	return path.Join(accountID(project, account), "keys", key)
}

func accountID(project, account string) string {
	return path.Join(projectID(project), "serviceAccounts", account)
}

func projectID(project string) string {
	return path.Join("projects", project)
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"testing"
)

func Test_NewMigration(t *testing.T) {
	var cases = []struct{
		directory string
		version uint64
		err bool
		output *Migration
	}{
		{
			directory: "unknown_directory",
			version: 0,
			err: true,
			output: nil,
		},
		{
			directory: "test",
			version: 0,
			err: true,
			output: nil,
		},
		{
			directory: "test",
			version: 1,
			err: false,
			output: &Migration{
				Name: "test/1_foo.sql",
				Version: 1,
				Description: "foo",
				AppliedAt: nil,
			},
		},
		{
			directory: "test",
			version: 2,
			err: true,
			output: nil,
		},
	}

	for n, c := range cases {
		migration, err := NewMigration(c.directory, c.version)
		if (err != nil) != c.err {
			t.Error("case", n, "got unexpected error:", err)
			continue
		}

		if !reflect.DeepEqual(migration, c.output) {
			t.Error("case", n, "got unexpected output:", migration)
		}
	}
}

func Test_Migration_Scan(t *testing.T) {
	var cases = []struct{
		migration Migration
		err bool
	}{
		{
			migration: Migration{
				Name: "test/1_foo.sql",
			},
			err: false,
		},
		{
			migration: Migration{
				Name: "test/0_unknown.sql",
			},
			err: true,
		},
	}

	for n, c := range cases {
		_, err := c.migration.Scan(`up`)
		if (err != nil) != c.err {
			t.Error("case", n, "got unexpected error:", err)
		}
	}
}
<commit_msg>Add scan unit tests<commit_after>package main

import (
	"reflect"
	"strings"
	"testing"
)

func Test_NewMigration(t *testing.T) {
	var cases = []struct{
		directory string
		version uint64
		err bool
		output *Migration
	}{
		{
			directory: "unknown_directory",
			version: 0,
			err: true,
			output: nil,
		},
		{
			directory: "test",
			version: 0,
			err: true,
			output: nil,
		},
		{
			directory: "test",
			version: 1,
			err: false,
			output: &Migration{
				Name: "test/1_foo.sql",
				Version: 1,
				Description: "foo",
				AppliedAt: nil,
			},
		},
		{
			directory: "test",
			version: 2,
			err: true,
			output: nil,
		},
	}

	for n, c := range cases {
		migration, err := NewMigration(c.directory, c.version)
		if (err != nil) != c.err {
			t.Error("case", n, "got unexpected error:", err)
			continue
		}

		if !reflect.DeepEqual(migration, c.output) {
			t.Error("case", n, "got unexpected output:", migration)
		}
	}
}

func Test_Migration_Scan(t *testing.T) {
	var cases = []struct{
		migration Migration
		err bool
	}{
		{
			migration: Migration{
				Name: "test/1_foo.sql",
			},
			err: false,
		},
		{
			migration: Migration{
				Name: "test/0_unknown.sql",
			},
			err: true,
		},
	}

	for n, c := range cases {
		_, err := c.migration.Scan(`up`)
		if (err != nil) != c.err {
			t.Error("case", n, "got unexpected error:", err)
		}
	}
}

func Test_scan(t *testing.T) {
	var cases = []struct{
		reader *strings.Reader
		section string
		output []string
	}{
		{
			reader: strings.NewReader(`-- rambler up
first
-- rambler up
second
-- rambler down
third
-- rambler up
fourth
`),
			section: "up",
			output: []string{"first", "second", "fourth"},
		},
		{
			reader: strings.NewReader(`-- rambler up
first
-- rambler up
second
-- rambler down
third
-- rambler up
fourth
`),
			section: "down",
			output: []string{"third"},
		},
	}

	for n, c := range cases {
		statements := scan(c.reader, c.section)
		if !reflect.DeepEqual(statements, c.output) {
			t.Error("case", n, "got unexpected output:", statements)
		}
	}
}

<|endoftext|>