<commit_before>// Here we GO!
// Thanks: http://learnxinyminutes.com/docs/go/

// Use package "main" to denote an executable, rather than a library
package main

// Reference libraries using import
import (
	"fmt" // Text formatting
	"net/http"	// Basic web server
	"strconv"	// String conversion
)

// Main is the entry point
func main() {
	// Basic line printing
	fmt.Println("Hello world!")

	// Call functions
	beyondHello()
	learnTypes()
	learnFlowControl()
	learnInterfaces()
	learnErrorHandling()
	learnConcurrency()
	learnWebProgramming()
}

// Another function
func beyondHello() {
	// Declare and initialize variable, long way
	var x int
	x = 3

	// Short method, automatic declare and infers type
	y := 4

	// Call a function which returns two values
	sum, prod := learnMultiple(x, y)
	fmt.Println("sum:", sum, "prod:", prod)
}

func learnMultiple(x, y int) (sum, prod int) {
	// Return two values
	return x + y, x * y
}

func learnTypes() {
	// Arrays: fixed size at compile time
	// Array of 4 integers
	var a4 [4]int
	// Array of 3 integers, initialized as shown
	a3 := [...]int{3, 1, 5}

	// Slices: dynamic size
	// Slice of 3 ints, no ellipsis
	s3 := []int{4, 5, 9}
	// Allocate slice of 4 ints, all 0
	s4 := make([]int, 4)
	// Turn string into byte slice
	bs := []byte("a slice")

	// Maps: dynamically growable associative array type
	m := map[string]int{
		"three": 3,
		"four":  4,
	}
	m["one"] = 1

	// Unused variables are an error, so we can use underscore to discard them
	_, _ = a4, s4

	// Output variables
	fmt.Println("a3:", a3, "s3:", s3, "bs:", bs, "m:", m)
}

func learnFlowControl() {
	// If statement
	if true {
		fmt.Println("true")
	} else {
		fmt.Println("false")
	}

	// Switch, cases don't fall through
	x := 1
	switch x {
		case 0:
		case 1:
			fmt.Println("ONE")
		case 2:
			// not used
	}

	// For: only loop statement in Go
	for x := 0; x < 3; x++ {
		fmt.Println("x:", x)
	}

	// Infinite loop
	for {
		// ... not really
		break
	}

	// Declare and assign y, then test
	if y := someComputation(); y > x {
		x = y
	}

	// Function literals are closures
	xBig := func() bool {
		// x automatically "enclosed" by closure
		return x > 100
	}

	fmt.Println("xBig:", xBig())
}

func someComputation() int {
	return 17
}

// Define interface
type Stringer interface {
	String() string
}

// Define a struct with two int fields
type pair struct {
	x, y int
}

// Define a method on pair, so pair now implements Stringer
func (p pair) String() string {
	return fmt.Sprintf("(%d, %d)", p.x, p.y)
}

func learnInterfaces() {
	// Braces: struct literal, evalutes to initialized struct
	p := pair{3, 4}
	fmt.Println(p.String())

	// Create instance of Stringer
	var i Stringer
	// Valid because pair implements Stringer
	i = p
	fmt.Println(i.String())

	// Create String method to implicitly print a struct
	fmt.Println("toString:", p)
}

func learnErrorHandling() {
	// ", ok" is an idiom to tell if something worked
	m := map[int]string{3: "three", 4: "four"}
	if x, ok := m[1]; !ok {
		fmt.Println("not found!")
	} else {
		fmt.Print(x)
	}

	// err is like an exception
	// Discard return value
	if _, err := strconv.Atoi("non-int"); err != nil {
		fmt.Println(err)
	}
}

// Increment x, send value to channel
func inc(x int, c chan int) {
	x++
	c <- x
}

func learnConcurrency() {
	// Make can allocate slices, maps, and channels
	// c is a channel
	c := make(chan int)

	// Start three concurrent goroutines, all incrementing to the same channel
	go inc(0, c)
	go inc(10, c)
	go inc(-805, c)

	// Read three results from channel and print
	// Result order cannot be predicted
	// <- is the "receive" operator
	fmt.Println("one:", <-c, "two:", <-c, "three:", <-c)

	// Create string channel, and channel of string channels
	cs := make(chan string)
	cc := make(chan chan string)

	// Anonymous goroutines
	go func() { c <- 84 }()
	go func() { cs <- "word" }()

	// Select is like a switch, but each case involves a channel operation
	// It will select a case at random out of cases that are ready to communicate
	// Select statement will run one time through only!
	select {
		// i can be assigned to value from int channel
		case i := <-c:
			fmt.Printf("it's a %T", i)
		// Value received, but can be discarded
		case <-cs:
			fmt.Println("Got a string!")
		// Empty channel, not ready for communication
		case <-cc:
			fmt.Println("This won't happen!")
	}
}

func learnWebProgramming() {
	// ListenAndServe HTTP server on specified port, second parameter is an interface http.Handler
	err := http.ListenAndServe(":8080", pair{})
	fmt.Println(err)
}

// Make pair a http.Handler by implementing its method
func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("You learned Go!"))
}
<commit_msg>go fmt learngo.go<commit_after>// Here we GO!
// Thanks: http://learnxinyminutes.com/docs/go/

// Use package "main" to denote an executable, rather than a library
package main

// Reference libraries using import
import (
	"fmt"      // Text formatting
	"net/http" // Basic web server
	"strconv"  // String conversion
)

// Main is the entry point
func main() {
	// Basic line printing
	fmt.Println("Hello world!")

	// Call functions
	beyondHello()
	learnTypes()
	learnFlowControl()
	learnInterfaces()
	learnErrorHandling()
	learnConcurrency()
	learnWebProgramming()
}

// Another function
func beyondHello() {
	// Declare and initialize variable, long way
	var x int
	x = 3

	// Short method, automatic declare and infers type
	y := 4

	// Call a function which returns two values
	sum, prod := learnMultiple(x, y)
	fmt.Println("sum:", sum, "prod:", prod)
}

func learnMultiple(x, y int) (sum, prod int) {
	// Return two values
	return x + y, x * y
}

func learnTypes() {
	// Arrays: fixed size at compile time
	// Array of 4 integers
	var a4 [4]int
	// Array of 3 integers, initialized as shown
	a3 := [...]int{3, 1, 5}

	// Slices: dynamic size
	// Slice of 3 ints, no ellipsis
	s3 := []int{4, 5, 9}
	// Allocate slice of 4 ints, all 0
	s4 := make([]int, 4)
	// Turn string into byte slice
	bs := []byte("a slice")

	// Maps: dynamically growable associative array type
	m := map[string]int{
		"three": 3,
		"four":  4,
	}
	m["one"] = 1

	// Unused variables are an error, so we can use underscore to discard them
	_, _ = a4, s4

	// Output variables
	fmt.Println("a3:", a3, "s3:", s3, "bs:", bs, "m:", m)
}

func learnFlowControl() {
	// If statement
	if true {
		fmt.Println("true")
	} else {
		fmt.Println("false")
	}

	// Switch, cases don't fall through
	x := 1
	switch x {
	case 0:
	case 1:
		fmt.Println("ONE")
	case 2:
		// not used
	}

	// For: only loop statement in Go
	for x := 0; x < 3; x++ {
		fmt.Println("x:", x)
	}

	// Infinite loop
	for {
		// ... not really
		break
	}

	// Declare and assign y, then test
	if y := someComputation(); y > x {
		x = y
	}

	// Function literals are closures
	xBig := func() bool {
		// x automatically "enclosed" by closure
		return x > 100
	}

	fmt.Println("xBig:", xBig())
}

func someComputation() int {
	return 17
}

// Define interface
type Stringer interface {
	String() string
}

// Define a struct with two int fields
type pair struct {
	x, y int
}

// Define a method on pair, so pair now implements Stringer
func (p pair) String() string {
	return fmt.Sprintf("(%d, %d)", p.x, p.y)
}

func learnInterfaces() {
	// Braces: struct literal, evalutes to initialized struct
	p := pair{3, 4}
	fmt.Println(p.String())

	// Create instance of Stringer
	var i Stringer
	// Valid because pair implements Stringer
	i = p
	fmt.Println(i.String())

	// Create String method to implicitly print a struct
	fmt.Println("toString:", p)
}

func learnErrorHandling() {
	// ", ok" is an idiom to tell if something worked
	m := map[int]string{3: "three", 4: "four"}
	if x, ok := m[1]; !ok {
		fmt.Println("not found!")
	} else {
		fmt.Print(x)
	}

	// err is like an exception
	// Discard return value
	if _, err := strconv.Atoi("non-int"); err != nil {
		fmt.Println(err)
	}
}

// Increment x, send value to channel
func inc(x int, c chan int) {
	x++
	c <- x
}

func learnConcurrency() {
	// Make can allocate slices, maps, and channels
	// c is a channel
	c := make(chan int)

	// Start three concurrent goroutines, all incrementing to the same channel
	go inc(0, c)
	go inc(10, c)
	go inc(-805, c)

	// Read three results from channel and print
	// Result order cannot be predicted
	// <- is the "receive" operator
	fmt.Println("one:", <-c, "two:", <-c, "three:", <-c)

	// Create string channel, and channel of string channels
	cs := make(chan string)
	cc := make(chan chan string)

	// Anonymous goroutines
	go func() { c <- 84 }()
	go func() { cs <- "word" }()

	// Select is like a switch, but each case involves a channel operation
	// It will select a case at random out of cases that are ready to communicate
	// Select statement will run one time through only!
	select {
	// i can be assigned to value from int channel
	case i := <-c:
		fmt.Printf("it's a %T", i)
	// Value received, but can be discarded
	case <-cs:
		fmt.Println("Got a string!")
	// Empty channel, not ready for communication
	case <-cc:
		fmt.Println("This won't happen!")
	}
}

func learnWebProgramming() {
	// ListenAndServe HTTP server on specified port, second parameter is an interface http.Handler
	err := http.ListenAndServe(":8080", pair{})
	fmt.Println(err)
}

// Make pair a http.Handler by implementing its method
func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("You learned Go!"))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	stdbuild "go/build"
	"time"

	"github.com/davecheney/gogo"
	"github.com/davecheney/gogo/build"
	"github.com/davecheney/gogo/log"
)

func init() {
	registerCommand("build", BuildCmd)
}

var (
	// build flags

	// should we build all packages in this project.
	// defaults to true when build is invoked from the project root.
	A bool

	// should we perform a release build +release tag ?
	// defaults to false, +debug.
	R bool
)

func addBuildFlags(fs *flag.FlagSet) {
	fs.BoolVar(&A, "a", false, "build all packages in this project")
	fs.BoolVar(&R, "r", false, "perform a release build")
}

var BuildCmd = &Command{
	Run: func(project *gogo.Project, args []string) error {
		t0 := time.Now()
		defer func() {
			log.Infof("build duration: %v", time.Since(t0))
		}()
		ctx, err := gogo.NewContext(project, *goroot, *goos, *goarch)
		if err != nil {
			return err
		}
		defer func() {
			log.Debugf("build statistics: %v", ctx.Statistics.String())
		}()
		var pkgs []*gogo.Package
		if A {
			var err error
			args, err = project.SrcPaths[0].AllPackages()
			if err != nil {
				return fmt.Errorf("could not fetch packages in srcpath %v: %v", project.SrcPaths[0], err)
			}
		}
		for _, arg := range args {
			pkg, err := ctx.ResolvePackage(arg)
			if err != nil {
				if _, ok := err.(*stdbuild.NoGoError); ok {
					log.Debugf("skipping %q", arg)
					continue
				}
				return fmt.Errorf("failed to resolve package %q: %v", arg, err)
			}
			pkgs = append(pkgs, pkg)
		}
		for _, pkg := range pkgs {
			if err := build.Build(ctx, pkg).Result(); err != nil {
				return err
			}
		}
		return ctx.Destroy()
	},
	AddFlags: addBuildFlags,
}
<commit_msg>Improve concurrency for gogo build<commit_after>package main

import (
	"flag"
	"fmt"
	stdbuild "go/build"
	"time"

	"github.com/davecheney/gogo"
	"github.com/davecheney/gogo/build"
	"github.com/davecheney/gogo/log"
)

func init() {
	registerCommand("build", BuildCmd)
}

var (
	// build flags

	// should we build all packages in this project.
	// defaults to true when build is invoked from the project root.
	A bool

	// should we perform a release build +release tag ?
	// defaults to false, +debug.
	R bool
)

func addBuildFlags(fs *flag.FlagSet) {
	fs.BoolVar(&A, "a", false, "build all packages in this project")
	fs.BoolVar(&R, "r", false, "perform a release build")
}

var BuildCmd = &Command{
	Run: func(project *gogo.Project, args []string) error {
		t0 := time.Now()
		defer func() {
			log.Infof("build duration: %v", time.Since(t0))
		}()
		ctx, err := gogo.NewContext(project, *goroot, *goos, *goarch)
		if err != nil {
			return err
		}
		defer func() {
			log.Debugf("build statistics: %v", ctx.Statistics.String())
		}()
		var pkgs []*gogo.Package
		if A {
			var err error
			args, err = project.SrcPaths[0].AllPackages()
			if err != nil {
				return fmt.Errorf("could not fetch packages in srcpath %v: %v", project.SrcPaths[0], err)
			}
		}
		for _, arg := range args {
			pkg, err := ctx.ResolvePackage(arg)
			if err != nil {
				if _, ok := err.(*stdbuild.NoGoError); ok {
					log.Debugf("skipping %q", arg)
					continue
				}
				return fmt.Errorf("failed to resolve package %q: %v", arg, err)
			}
			pkgs = append(pkgs, pkg)
		}
		results := make(chan gogo.Future, len(pkgs))
		go func() {
			defer close(results)
			for _, pkg := range pkgs {
				results <- build.Build(ctx, pkg)
			}
		}()
		for result := range results {
			if err := result.Result(); err != nil {
				return err
			}
		}
		return ctx.Destroy()
	},
	AddFlags: addBuildFlags,
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)

const eof = -1
const (
	leftDelim    = "{"
	rightDelim   = "}"
	leftComment  = "/*"
	rightComment = "*/"
	lineComment  = "//"
)

type Pos int

// reserved words and other tokens we care about
const (
	tokFunc     = "func"
	tokOverride = "override"
	tokClass    = "class"
	tokExtends  = "extends"
	tokPackage  = "package"
)

//go:generate stringer -type=itemType
type itemType int

type item struct {
	typ itemType
	val string
}

const (
	itemError itemType = iota
	itemDot
	itemEOF
	itemClass
	itemExtends
	itemOpenBrace
	itemCloseBrace
	itemFunc
	itemOverride
	itemText
	itemLeftDelim
	itemRightDelim
	itemFuncBody
	itemFuncParams
	itemMember
	itemComment
	itemLineComment
	itemPackage
)

func (i item) String() string {
	switch i.typ {
	case itemError:
		return i.val

	case itemEOF:
		return "EOF"

	}

	/*
		if len(i.val) > 10 {
			return fmt.Sprintf("%.10q...", i.val)
		}
	*/

	return fmt.Sprintf("%v: %q\n", i.typ, i.val)
}

type lexer struct {
	input   string    // string being scanned
	start   int       // start position of item
	pos     int       // current position
	lastPos int       // last position of item read
	width   int       // width of last rune
	items   chan item // channel of scanned items
}

type stateFn func(*lexer) stateFn

func (l *lexer) run() {
	for state := lexText; state != nil; {
		state = state(l)
	}
	close(l.items)
}

// nextItem returns the next item from the input.
// Called by the parser, not in the lexing goroutine.
func (l *lexer) nextItem() item {
	item, ok := <-l.items

	if !ok {
		panic("Read past end of file")
	}

	l.lastPos = l.pos

	return item
}

func lex(input string) *lexer {
	l := &lexer{
		input: input,
		items: make(chan item),
	}
	go l.run()
	return l
}

func (l *lexer) emit(t itemType) {
	item := item{t, l.input[l.start:l.pos]}
	l.items <- item
	l.start = l.pos
	//fmt.Printf("%v", item)

}

func lexText(l *lexer) stateFn {
	for {
		l.acceptSpace()
		if strings.HasPrefix(l.input[l.pos:], tokClass+" ") {
			if l.pos > l.start {
				l.emit(itemText) // emit text already read so far for straight output
			}
			return lexClass
		} else if strings.HasPrefix(l.input[l.pos:], leftComment) {
			if l.pos > l.start {
				l.emit(itemText) // emit text already read so far for straight output
			}
			return lexComment(l, lexText)
		} else if strings.HasPrefix(l.input[l.pos:], tokPackage) {
			return lexIdentifier(l, itemPackage, lexText)
		}

		l.nextLine()

		if l.peek() == eof {
			break
		}

	}

	if l.pos > l.start {
		l.emit(itemText)
	}
	l.emit(itemEOF)
	return nil // stop
}

// lexComment scans a comment. The left comment marker is known to be present.
func lexComment(l *lexer, nextState stateFn) stateFn {
	l.pos += int(len(leftComment))
	i := strings.Index(l.input[l.pos:], rightComment)
	if i < 0 {
		return l.errorf("unclosed comment")
	}
	l.pos += int(i + len(rightComment))
	l.emit(itemComment)
	return nextState
}

func lexClass(l *lexer) stateFn {
	l.pos += len(tokClass)
	l.pos += 1 // consume space
	return lexIdentifier(l, itemClass, lexExtends)
}

func (l *lexer) next() rune {
	var c rune
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	c, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	l.pos += l.width
	return c
}

func (l *lexer) nextLine() {
	for {
		r := l.next()
		if r == '\n' || r == eof {
			return
		}
	}
}

func (l *lexer) backup() {
	l.pos -= l.width
}

func (l *lexer) ignore() {
	l.start = l.pos
}

func (l *lexer) ignoreSpace() {
	for {
		r := l.next()
		switch {
		case r == eof:
			return
		case isSpace(r):
			l.ignore()
		default:
			l.backup()
			return
		}
	}
}

func lexIdentifier(l *lexer, typ itemType, nextState stateFn) stateFn {
	var r rune

	l.ignore()
	l.ignoreSpace()

	for {
		switch r = l.next(); {
		case r == eof || r == '\n':
			return l.errorf("Missing identifier")
		case !isIdChar(r):
			if l.pos == l.start {
				return l.errorf("Invalid identifier")
			}
			l.backup()
			l.emit(typ)
			return nextState
		}
	}
}

// expecting "extends" keyword
func lexExtends(l *lexer) stateFn {
	l.ignoreSpace()
	if !strings.HasPrefix(l.input[l.pos:], tokExtends) {
		l.errorf("Missing 'extends' keyword")
	}
	l.pos += len(tokExtends)
	return lexExtendsClassName
}

func lexExtendsClassName(l *lexer) stateFn {
	return lexIdentifier(l, itemExtends, lexBodyOpen)
}

func lexBodyOpen(l *lexer) stateFn {
	//l.parenDepth = 0
	lexLeftDelim(l)
	return lexClassBody
}

func lexLeftDelim(l *lexer) {
	l.ignoreSpace()
	l.pos += int(len(leftDelim))
	l.emit(itemLeftDelim)
	//l.parenDepth ++
}

func lexRightDelim(l *lexer) {
	l.pos += int(len(rightDelim))
	l.emit(itemRightDelim)
	//l.parenDepth --
}

func lexClassBody(l *lexer) stateFn {
	l.ignoreSpace()
	if strings.HasPrefix(l.input[l.pos:], rightDelim) {
		return lexClassClose
	}

	if strings.HasPrefix(l.input[l.pos:], tokFunc+" ") {
		return lexFunc
	}

	if strings.HasPrefix(l.input[l.pos:], tokOverride+" ") {
		return lexOverride
	}

	if strings.HasPrefix(l.input[l.pos:], leftComment) {
		return lexComment(l, lexClassBody)
	}

	if strings.HasPrefix(l.input[l.pos:], lineComment) {
		l.nextLine()
		l.emit(itemLineComment)
		return lexClassBody
	}

	return lexMember
}

func lexClassClose(l *lexer) stateFn {
	lexRightDelim(l)
	return lexText
}

/**
Lex the override keyword. We know the "override" keyword is at the beginning of the stream.
*/
func lexOverride(l *lexer) stateFn {
	l.pos += len(tokOverride)
	l.start = l.pos
	l.ignoreSpace()

	if !strings.HasPrefix(l.input[l.pos:], tokFunc+" ") {
		return l.errorf("Missing 'func' keyword after override")
	}

	l.emit(itemOverride)

	return lexFunc
}

/**
Lex a function. We know the "func" keyword is at the beginning of the stream.
*/
func lexFunc(l *lexer) stateFn {
	l.pos += len(tokFunc)
	l.start = l.pos
	l.ignoreSpace()
	return lexIdentifier(l, itemFunc, lexFuncParams)
}

/**
Lex a function parameter list, including the return parameters. We need this because it will become part of the interface
definition and the struct definition.
*/
func lexFuncParams(l *lexer) stateFn {
	_ = "breakpoint"
	if l.peek() != '(' {
		return l.errorf("Expected opening parenthesis for function parameter list.")
	}

	acceptUntil(l, ")")
	l.next()
	l.acceptSpace()

	if l.peek() == '(' {
		// found return params in parens
		acceptUntil(l, ")")
		l.next()
	} else if l.peek() == '{' {
		// do nothing, found start of body
	} else {
		// found a return param, which might be an interface or annonymous struct declaration
		word := l.acceptIdentifier()
		if word == "struct" || word == "interface" {
			acceptUntil(l, rightDelim)
			l.acceptSpace()
			if l.peek() != '{' {
				return l.errorf("Missing opening bracket for function.")
			}
		}

	}

	l.emit(itemFuncParams)
	return lexFuncBody
}

/**
 */
func lexFuncBody(l *lexer) stateFn {
	// first find a left delim
	var r rune

	// TODO: Skip comments and quoted strings
	for r = l.next(); r != '{' && r != eof; r = l.next() {
	}
	var parenDepth = 1
	for parenDepth > 0 {
		r = l.next()
		if r == '{' {
			parenDepth++
		} else if r == '}' {
			parenDepth--
		} else if r == eof {
			return l.errorf("Unexpected EOF. Function body is still open.")
		}
	}
	l.emit(itemFuncBody)
	return lexClassBody
}

func lexMember(l *lexer) stateFn {
	l.nextLine()
	if l.start+1 < l.pos {
		l.emit(itemMember)
	} else {
		l.ignore()
	}
	return lexClassBody
}

// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
	return r == ' ' || r == '\t'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

// isIdChar reports whether r is character that can be part of an identifier, specifically on a func definition line
func isIdChar(r rune) bool {
	return r == '.' || r == '_' || r == '*' || unicode.IsLetter(r) || unicode.IsDigit(r)
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, fmt.Sprintf(format, args...)}
	return nil
}

// peek returns but does not consume the next rune in the input.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

func acceptUntil(l *lexer, terminators string) {
	for strings.IndexRune(terminators, l.next()) < 0 {
	}
	l.backup()
}

func (l *lexer) acceptIdentifier() string {
	startPos := l.pos
	for {
		r := l.next()
		if !isIdChar(r) {
			l.backup()
			return l.input[startPos:l.pos]
		}
	}
}

func (l *lexer) acceptSpace() {
	for isSpace(l.next()) {
	}
	l.backup()
}
<commit_msg>Adding ability to return slices and maps<commit_after>package main

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)

const eof = -1
const (
	leftDelim    = "{"
	rightDelim   = "}"
	leftComment  = "/*"
	rightComment = "*/"
	lineComment  = "//"
)

type Pos int

// reserved words and other tokens we care about
const (
	tokFunc     = "func"
	tokOverride = "override"
	tokClass    = "class"
	tokExtends  = "extends"
	tokPackage  = "package"
)

//go:generate stringer -type=itemType
type itemType int

type item struct {
	typ itemType
	val string
}

const (
	itemError itemType = iota
	itemDot
	itemEOF
	itemClass
	itemExtends
	itemOpenBrace
	itemCloseBrace
	itemFunc
	itemOverride
	itemText
	itemLeftDelim
	itemRightDelim
	itemFuncBody
	itemFuncParams
	itemMember
	itemComment
	itemLineComment
	itemPackage
)

func (i item) String() string {
	switch i.typ {
	case itemError:
		return i.val

	case itemEOF:
		return "EOF"

	}

	/*
		if len(i.val) > 10 {
			return fmt.Sprintf("%.10q...", i.val)
		}
	*/

	return fmt.Sprintf("%v: %q\n", i.typ, i.val)
}

type lexer struct {
	input   string    // string being scanned
	start   int       // start position of item
	pos     int       // current position
	lastPos int       // last position of item read
	width   int       // width of last rune
	items   chan item // channel of scanned items
}

type stateFn func(*lexer) stateFn

func (l *lexer) run() {
	for state := lexText; state != nil; {
		state = state(l)
	}
	close(l.items)
}

// nextItem returns the next item from the input.
// Called by the parser, not in the lexing goroutine.
func (l *lexer) nextItem() item {
	item, ok := <-l.items

	if !ok {
		panic("Read past end of file")
	}

	l.lastPos = l.pos

	return item
}

func lex(input string) *lexer {
	l := &lexer{
		input: input,
		items: make(chan item),
	}
	go l.run()
	return l
}

func (l *lexer) emit(t itemType) {
	item := item{t, l.input[l.start:l.pos]}
	l.items <- item
	l.start = l.pos
	//fmt.Printf("%v", item)

}

func lexText(l *lexer) stateFn {
	for {
		l.acceptSpace()
		if strings.HasPrefix(l.input[l.pos:], tokClass+" ") {
			if l.pos > l.start {
				l.emit(itemText) // emit text already read so far for straight output
			}
			return lexClass
		} else if strings.HasPrefix(l.input[l.pos:], leftComment) {
			if l.pos > l.start {
				l.emit(itemText) // emit text already read so far for straight output
			}
			return lexComment(l, lexText)
		} else if strings.HasPrefix(l.input[l.pos:], tokPackage) {
			return lexIdentifier(l, itemPackage, lexText)
		}

		l.nextLine()

		if l.peek() == eof {
			break
		}

	}

	if l.pos > l.start {
		l.emit(itemText)
	}
	l.emit(itemEOF)
	return nil // stop
}

// lexComment scans a comment. The left comment marker is known to be present.
func lexComment(l *lexer, nextState stateFn) stateFn {
	l.pos += int(len(leftComment))
	i := strings.Index(l.input[l.pos:], rightComment)
	if i < 0 {
		return l.errorf("unclosed comment")
	}
	l.pos += int(i + len(rightComment))
	l.emit(itemComment)
	return nextState
}

func lexClass(l *lexer) stateFn {
	l.pos += len(tokClass)
	l.pos += 1 // consume space
	return lexIdentifier(l, itemClass, lexExtends)
}

func (l *lexer) next() rune {
	var c rune
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	c, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	l.pos += l.width
	return c
}

func (l *lexer) nextLine() {
	for {
		r := l.next()
		if r == '\n' || r == eof {
			return
		}
	}
}

func (l *lexer) backup() {
	l.pos -= l.width
}

func (l *lexer) ignore() {
	l.start = l.pos
}

func (l *lexer) ignoreSpace() {
	for {
		r := l.next()
		switch {
		case r == eof:
			return
		case isSpace(r):
			l.ignore()
		default:
			l.backup()
			return
		}
	}
}

func lexIdentifier(l *lexer, typ itemType, nextState stateFn) stateFn {
	var r rune

	l.ignore()
	l.ignoreSpace()

	for {
		switch r = l.next(); {
		case r == eof || r == '\n':
			return l.errorf("Missing identifier")
		case !isIdChar(r):
			if l.pos == l.start {
				return l.errorf("Invalid identifier")
			}
			l.backup()
			l.emit(typ)
			return nextState
		}
	}
}

// expecting "extends" keyword
func lexExtends(l *lexer) stateFn {
	l.ignoreSpace()
	if !strings.HasPrefix(l.input[l.pos:], tokExtends) {
		l.errorf("Missing 'extends' keyword")
	}
	l.pos += len(tokExtends)
	return lexExtendsClassName
}

func lexExtendsClassName(l *lexer) stateFn {
	return lexIdentifier(l, itemExtends, lexBodyOpen)
}

func lexBodyOpen(l *lexer) stateFn {
	//l.parenDepth = 0
	lexLeftDelim(l)
	return lexClassBody
}

func lexLeftDelim(l *lexer) {
	l.ignoreSpace()
	l.pos += int(len(leftDelim))
	l.emit(itemLeftDelim)
	//l.parenDepth ++
}

func lexRightDelim(l *lexer) {
	l.pos += int(len(rightDelim))
	l.emit(itemRightDelim)
	//l.parenDepth --
}

func lexClassBody(l *lexer) stateFn {
	l.ignoreSpace()
	if strings.HasPrefix(l.input[l.pos:], rightDelim) {
		return lexClassClose
	}

	if strings.HasPrefix(l.input[l.pos:], tokFunc+" ") {
		return lexFunc
	}

	if strings.HasPrefix(l.input[l.pos:], tokOverride+" ") {
		return lexOverride
	}

	if strings.HasPrefix(l.input[l.pos:], leftComment) {
		return lexComment(l, lexClassBody)
	}

	if strings.HasPrefix(l.input[l.pos:], lineComment) {
		l.nextLine()
		l.emit(itemLineComment)
		return lexClassBody
	}

	return lexMember
}

func lexClassClose(l *lexer) stateFn {
	lexRightDelim(l)
	return lexText
}

/**
Lex the override keyword. We know the "override" keyword is at the beginning of the stream.
*/
func lexOverride(l *lexer) stateFn {
	l.pos += len(tokOverride)
	l.start = l.pos
	l.ignoreSpace()

	if !strings.HasPrefix(l.input[l.pos:], tokFunc+" ") {
		return l.errorf("Missing 'func' keyword after override")
	}

	l.emit(itemOverride)

	return lexFunc
}

/**
Lex a function. We know the "func" keyword is at the beginning of the stream.
*/
func lexFunc(l *lexer) stateFn {
	l.pos += len(tokFunc)
	l.start = l.pos
	l.ignoreSpace()
	return lexIdentifier(l, itemFunc, lexFuncParams)
}

/**
Lex a function parameter list, including the return parameters. We need this because it will become part of the interface
definition and the struct definition.
*/
func lexFuncParams(l *lexer) stateFn {
	_ = "breakpoint"
	if l.peek() != '(' {
		return l.errorf("Expected opening parenthesis for function parameter list.")
	}

	acceptUntil(l, ")")
	l.next()
	l.acceptSpace()

	if l.peek() == '(' {
		// found return params in parens
		acceptUntil(l, ")")
		l.next()
	} else if l.peek() == '{' {
		// do nothing, found start of body
	} else {
		// found a return param, which might be an interface or annonymous struct declaration
		word := l.acceptIdentifier()
		if word == "struct" || word == "interface" {
			acceptUntil(l, rightDelim)
			l.acceptSpace()
			if l.peek() != '{' {
				return l.errorf("Missing opening bracket for function.")
			}
		}

	}

	l.emit(itemFuncParams)
	return lexFuncBody
}

/**
 */
func lexFuncBody(l *lexer) stateFn {
	// first find a left delim
	var r rune

	// TODO: Skip comments and quoted strings
	for r = l.next(); r != '{' && r != eof; r = l.next() {
	}
	var parenDepth = 1
	for parenDepth > 0 {
		r = l.next()
		if r == '{' {
			parenDepth++
		} else if r == '}' {
			parenDepth--
		} else if r == eof {
			return l.errorf("Unexpected EOF. Function body is still open.")
		}
	}
	l.emit(itemFuncBody)
	return lexClassBody
}

func lexMember(l *lexer) stateFn {
	l.nextLine()
	if l.start+1 < l.pos {
		l.emit(itemMember)
	} else {
		l.ignore()
	}
	return lexClassBody
}

// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
	return r == ' ' || r == '\t'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

// isIdChar reports whether r is character that can be part of an identifier, specifically on a func definition line
func isIdChar(r rune) bool {
	return r == '.' ||
		r == '_' ||
		r == '*' ||
		r == '[' ||
		r == ']' ||
		unicode.IsLetter(r) ||
		unicode.IsDigit(r)
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, fmt.Sprintf(format, args...)}
	return nil
}

// peek returns but does not consume the next rune in the input.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

func acceptUntil(l *lexer, terminators string) {
	for strings.IndexRune(terminators, l.next()) < 0 {
	}
	l.backup()
}

func (l *lexer) acceptIdentifier() string {
	startPos := l.pos
	for {
		r := l.next()
		if !isIdChar(r) {
			l.backup()
			return l.input[startPos:l.pos]
		}
	}
}

func (l *lexer) acceptSpace() {
	for isSpace(l.next()) {
	}
	l.backup()
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"gopkg.in/fsnotify.v1"

	"github.com/influx6/assets"
	"github.com/influx6/flux"
	"github.com/influx6/reactors/builders"
	"github.com/influx6/reactors/fs"
)

// RegisterDefaultPlugins provides a set of default plugins for relay
func RegisterDefaultPlugins(pm *PluginManager) {
	addBuilder(pm)
	addGoFriday(pm)
	addGoStaticBundle(pm)
	addJSWatchBuild(pm)
	addWatchBuildRun(pm)
	addCommander(pm)
}

func addBuilder(pm *PluginManager) {
	//these are internally used
	pm.Add("builder", func(config *BuildConfig, options Plugins, c chan bool) {
		pwd, _ := os.Getwd()
		_, binName := filepath.Split(config.Package)
		// bin := filepath.Join(pwd, config.Bin)
		var clientdir string

		outputdir := filepath.Join(pwd, config.Client.StaticDir)

		if config.Client.Dir != "" {
			clientdir = filepath.Join(pwd, config.Client.Dir)
		}

		goget := builders.GoInstallerWith("./")

		jsbuild := builders.JSLauncher(builders.JSBuildConfig{
			Package:    config.ClientPackage,
			Folder:     outputdir,
			FileName:   config.Client.Name,
			Tags:       config.Client.BuildTags,
			Verbose:    config.Client.UseVerbose,
			PackageDir: clientdir,
		})

		gobuild := builders.GoBuilderWith(builders.BuildConfig{
			Path: filepath.Join(pwd, config.Bin),
			Name: binName,
			Args: config.BinArgs,
		})

		goget.Bind(jsbuild, true)

		//send out the build command after js build
		jsbuild.React(func(root flux.Reactor, _ error, _ interface{}) {
			gobuild.Send(true)
		}, true)

		//run go installer
		goget.Send(true)

		flux.GoDefer("watchBuildRun:kill", func() {
			<-c
			//close our builders
			goget.Close()
			gobuild.Close()
		})
	})
}

func addWatchBuildRun(pm *PluginManager) {
	//these are internally used
	pm.Add("watchBuildRun", func(config *BuildConfig, options Plugins, c chan bool) {
		pwd, _ := os.Getwd()
		_, binName := filepath.Split(config.Package)
		binDir := filepath.Join(pwd, config.Bin)
		binfile := filepath.Join(binDir, binName)

		pkgs := append([]string{}, config.Package, "github.com/influx6/relay/relay", "github.com/influx6/relay/engine")

		packages, err := assets.GetAllPackageLists(pkgs)

		if err != nil {
			panic(err)
		}

		fmt.Printf("--> Retrieved package directories %s \n", config.Package)

		goget := builders.GoInstallerWith("./")

		buildbin := builders.BinaryBuildLauncher(builders.BinaryBuildConfig{
			Path:    binDir,
			Name:    binName,
			RunArgs: config.BinArgs,
		})

		goget.Bind(buildbin, true)

		fmt.Printf("--> Initializing File Watcher using package dependecies at %d\n", len(packages))

		watcher := fs.WatchSet(fs.WatchSetConfig{
			Path: packages,
			Validator: func(base string, info os.FileInfo) bool {
				if strings.Contains(base, ".git") {
					return false
				}

				if strings.Contains(base, binDir) || base == binDir {
					return false
				}

				if strings.Contains(base, binfile) || base == binfile {
					return false
				}

				if info != nil && info.IsDir() {
					return true
				}

				if filepath.Ext(base) != ".go" {
					return false
				}

				return true
			},
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> File as changed: %+s\n", ev.String())
			}
		}), true)

		watcher.Bind(goget, true)

		fmt.Printf("--> Sending signal for 'go get'\n")
		//run go installer
		goget.Send(true)

		fmt.Printf("--> Initializing Interrupt Signal  Watcher for %s@%s\n", binName, binfile)

		flux.GoDefer("watchBuildRun:kill", func() {
			<-c
			//close our builders
			watcher.Close()
			goget.Close()
			buildbin.Close()
		})
	})
}

func addJSWatchBuild(pm *PluginManager) {
	//these are internally used for js building
	pm.Add("jsWatchBuild", func(config *BuildConfig, options Plugins, c chan bool) {
		pwd, _ := os.Getwd()
		_, binName := filepath.Split(config.Package)
		binDir := filepath.Join(pwd, config.Bin)
		binfile := filepath.Join(binDir, binName)

		pkgs := append([]string{}, config.ClientPackage)

		packages, err := assets.GetAllPackageLists(pkgs)

		if err != nil {
			panic(err)
		}

		// packages = append(packages, pwd)
		fmt.Printf("--> Retrieved js package directories %s \n", config.Package)

		var clientdir string

		outputdir := filepath.Join(pwd, config.Client.StaticDir)

		if config.Client.Dir != "" {
			clientdir = filepath.Join(pwd, config.Client.Dir)
		}

		jsbuild := builders.JSLauncher(builders.JSBuildConfig{
			Package:    config.ClientPackage,
			Folder:     outputdir,
			FileName:   config.Client.Name,
			Tags:       config.Client.BuildTags,
			Verbose:    config.Client.UseVerbose,
			PackageDir: clientdir,
		})

		fmt.Printf("--> Initializing File Watcher using js package dependecies at %d\n", len(packages))

		watcher := fs.WatchSet(fs.WatchSetConfig{
			Path: packages,
			Validator: func(base string, info os.FileInfo) bool {
				if strings.Contains(base, ".git") {
					return false
				}

				if strings.Contains(base, binDir) || base == binDir {
					return false
				}

				if strings.Contains(base, binfile) || base == binfile {
					return false
				}

				if info != nil && info.IsDir() {
					return true
				}

				if filepath.Ext(base) != ".go" {
					return false
				}

				// log.Printf("allowed: %s", base)
				return true
			},
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> Client:File as changed: %+s\n", ev.String())
			}
		}), true)

		watcher.Bind(jsbuild, true)

		jsbuild.Send(true)

		flux.GoDefer("jsWatchBuild:kill", func() {
			<-c
			//close our builders
			watcher.Close()
			jsbuild.Close()
		})

	})

}

func addGoFriday(pm *PluginManager) {
	pm.Add("goFriday", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format

		      tag: gofriday
		      config:
		        markdown: ./markdown
		        templates: ./templates

		  		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		markdownDir := options.Config["markdown"]
		templateDir := options.Config["templates"]

		//optional args
		ext := options.Config["ext"]
		//must be a bool
		sanitizeString := options.Config["sanitize"]

		var sanitize bool

		if svz, err := strconv.ParseBool(sanitizeString); err == nil {
			sanitize = svz
		}

		if markdownDir == "" || templateDir == "" {
			fmt.Println("---> gofriday.error: expected to find keys (markdown and templates) in config map")
			return
		}

		//get the absolute path
		absDir := filepath.Join(pwd, markdownDir)
		tbsDir := filepath.Join(pwd, templateDir)

		gofriday, err := builders.GoFridayStream(builders.MarkStreamConfig{
			InputDir: absDir,
			SaveDir:  tbsDir,
			Ext:      ext,
			Sanitize: sanitize,
		})

		if err != nil {
			fmt.Printf("---> gofriday.error: %s", err)
			return
		}

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> goFriday:File as changed: %+s\n", ev.String())
			}
		}), true)
		// create the command runner set to run the args
		watcher.Bind(gofriday, true)

		flux.GoDefer("goFiday:kill", func() {
			<-c
			watcher.Close()
		})
	})
}

func addGoStaticBundle(pm *PluginManager) {
	pm.Add("goStatic", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format: you can control all aspects of the assets.BindFS using the following

		      tag: gostatic
					# add commands to run on file changes
					args:
						- touch ./templates/smirf.go
		      config:
		        in: ./markdown
		        out: ./templates
						package: smirf
						file: smirf
						gzipped: true
						nodecompression: true
						production: true // generally you want to leave this to the cli to set

		  		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		inDir := options.Config["in"]
		outDir := options.Config["out"]
		packageName := options.Config["package"]
		fileName := options.Config["file"]
		absDir := filepath.Join(pwd, inDir)
		absFile := filepath.Join(pwd, outDir, fileName+".go")

		if inDir == "" || outDir == "" || packageName == "" || fileName == "" {
			fmt.Println("---> goStatic.error: the following keys(in,out,package,file) must not be empty")
			return
		}

		//set up the boolean values
		var prod bool
		var gzip bool
		var nodcom bool
		var err error

		if gz, err := strconv.ParseBool(options.Config["gzipped"]); err == nil {
			gzip = gz
		} else {
			if config.Mode > 0 {
				gzip = true
			}
		}

		if br, err := strconv.ParseBool(options.Config["nodecompression"]); err == nil {
			nodcom = br
		}

		if pr, err := strconv.ParseBool(options.Config["production"]); err == nil {
			prod = pr
		} else {
			if config.Mode <= 0 {
				prod = false
			} else {
				prod = true
			}
		}

		gostatic, err := builders.BundleAssets(&assets.BindFSConfig{
			InDir:           inDir,
			OutDir:          outDir,
			Package:         packageName,
			File:            fileName,
			Gzipped:         gzip,
			NoDecompression: nodcom,
			Production:      prod,
		})

		if err != nil {
			fmt.Printf("---> goStatic.error: %s", err)
			return
		}

		//bundle up the assets for the main time
		gostatic.Send(true)

		var command []string

		if runtime.GOOS == "windows" {
			// command = append(command, fmt.Sprintf("copy /b %s +,,", absFile))
			command = append(command, fmt.Sprintf("powershell  (ls %s).LastWriteTime = Get-Date", absFile))
		} else {
			command = append(command, fmt.Sprintf("touch %s", absFile))
		}

		//add the args from the options
		command = append(command, options.Args...)
		// log.Printf("command %s", command)

		//adds a CommandLauncher to touch the output file to force a file change notification
		touchCommand := builders.CommandLauncher(command)
		gostatic.Bind(touchCommand, true)

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		// create the command runner set to run the args
		watcher.Bind(gostatic, true)

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> goStatic:File as changed: %+s\n", ev.String())
			}
		}), true)

		flux.GoDefer("goStatic:kill", func() {
			<-c
			gostatic.Close()
		})
	})
}

func addCommander(pm *PluginManager) {
	pm.Add("commandWatch", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format

		  tag: dirWatch
		  config:
		    path: "./static/less"
		  args:
		    - lessc ./static/less/main.less ./static/css/main.css
		    - lessc ./static/less/svg.less ./static/css/svg.css

		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		dir := options.Config["path"]

		//get the command we should run on change
		commands := options.Args

		if dir == "" {
			fmt.Printf("---> dirWatch.error: no path set in config map for plug")
			return
		}

		//get the absolute path
		absDir := filepath.Join(pwd, dir)

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> commandWatch:File as changed: %+s\n", ev.String())
			}
		}), true)
		// create the command runner set to run the args
		watcher.Bind(builders.CommandLauncher(commands), true)

		flux.GoDefer("CommandWatch:kill", func() {
			<-c
			watcher.Close()
		})
	})
}
<commit_msg>updated assets: switched to touch<commit_after>package cli

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"gopkg.in/fsnotify.v1"

	"github.com/influx6/assets"
	"github.com/influx6/flux"
	"github.com/influx6/reactors/builders"
	"github.com/influx6/reactors/fs"
)

// RegisterDefaultPlugins provides a set of default plugins for relay
func RegisterDefaultPlugins(pm *PluginManager) {
	addBuilder(pm)
	addGoFriday(pm)
	addGoStaticBundle(pm)
	addJSWatchBuild(pm)
	addWatchBuildRun(pm)
	addCommander(pm)
}

func addBuilder(pm *PluginManager) {
	//these are internally used
	pm.Add("builder", func(config *BuildConfig, options Plugins, c chan bool) {
		pwd, _ := os.Getwd()
		_, binName := filepath.Split(config.Package)
		// bin := filepath.Join(pwd, config.Bin)
		var clientdir string

		outputdir := filepath.Join(pwd, config.Client.StaticDir)

		if config.Client.Dir != "" {
			clientdir = filepath.Join(pwd, config.Client.Dir)
		}

		goget := builders.GoInstallerWith("./")

		jsbuild := builders.JSLauncher(builders.JSBuildConfig{
			Package:    config.ClientPackage,
			Folder:     outputdir,
			FileName:   config.Client.Name,
			Tags:       config.Client.BuildTags,
			Verbose:    config.Client.UseVerbose,
			PackageDir: clientdir,
		})

		gobuild := builders.GoBuilderWith(builders.BuildConfig{
			Path: filepath.Join(pwd, config.Bin),
			Name: binName,
			Args: config.BinArgs,
		})

		goget.Bind(jsbuild, true)

		//send out the build command after js build
		jsbuild.React(func(root flux.Reactor, _ error, _ interface{}) {
			gobuild.Send(true)
		}, true)

		//run go installer
		goget.Send(true)

		flux.GoDefer("watchBuildRun:kill", func() {
			<-c
			//close our builders
			goget.Close()
			gobuild.Close()
		})
	})
}

func addWatchBuildRun(pm *PluginManager) {
	//these are internally used
	pm.Add("watchBuildRun", func(config *BuildConfig, options Plugins, c chan bool) {
		pwd, _ := os.Getwd()
		_, binName := filepath.Split(config.Package)
		binDir := filepath.Join(pwd, config.Bin)
		binfile := filepath.Join(binDir, binName)

		pkgs := append([]string{}, config.Package, "github.com/influx6/relay/relay", "github.com/influx6/relay/engine")

		packages, err := assets.GetAllPackageLists(pkgs)

		if err != nil {
			panic(err)
		}

		fmt.Printf("--> Retrieved package directories %s \n", config.Package)

		goget := builders.GoInstallerWith("./")

		buildbin := builders.BinaryBuildLauncher(builders.BinaryBuildConfig{
			Path:    binDir,
			Name:    binName,
			RunArgs: config.BinArgs,
		})

		goget.Bind(buildbin, true)

		fmt.Printf("--> Initializing File Watcher using package dependecies at %d\n", len(packages))

		watcher := fs.WatchSet(fs.WatchSetConfig{
			Path: packages,
			Validator: func(base string, info os.FileInfo) bool {
				if strings.Contains(base, ".git") {
					return false
				}

				if strings.Contains(base, binDir) || base == binDir {
					return false
				}

				if strings.Contains(base, binfile) || base == binfile {
					return false
				}

				if info != nil && info.IsDir() {
					return true
				}

				if filepath.Ext(base) != ".go" {
					return false
				}

				return true
			},
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> File as changed: %+s\n", ev.String())
			}
		}), true)

		watcher.Bind(goget, true)

		fmt.Printf("--> Sending signal for 'go get'\n")
		//run go installer
		goget.Send(true)

		fmt.Printf("--> Initializing Interrupt Signal  Watcher for %s@%s\n", binName, binfile)

		flux.GoDefer("watchBuildRun:kill", func() {
			<-c
			//close our builders
			watcher.Close()
			goget.Close()
			buildbin.Close()
		})
	})
}

func addJSWatchBuild(pm *PluginManager) {
	//these are internally used for js building
	pm.Add("jsWatchBuild", func(config *BuildConfig, options Plugins, c chan bool) {
		pwd, _ := os.Getwd()
		_, binName := filepath.Split(config.Package)
		binDir := filepath.Join(pwd, config.Bin)
		binfile := filepath.Join(binDir, binName)

		pkgs := append([]string{}, config.ClientPackage)

		packages, err := assets.GetAllPackageLists(pkgs)

		if err != nil {
			panic(err)
		}

		// packages = append(packages, pwd)
		fmt.Printf("--> Retrieved js package directories %s \n", config.Package)

		var clientdir string

		outputdir := filepath.Join(pwd, config.Client.StaticDir)

		if config.Client.Dir != "" {
			clientdir = filepath.Join(pwd, config.Client.Dir)
		}

		jsbuild := builders.JSLauncher(builders.JSBuildConfig{
			Package:    config.ClientPackage,
			Folder:     outputdir,
			FileName:   config.Client.Name,
			Tags:       config.Client.BuildTags,
			Verbose:    config.Client.UseVerbose,
			PackageDir: clientdir,
		})

		fmt.Printf("--> Initializing File Watcher using js package dependecies at %d\n", len(packages))

		watcher := fs.WatchSet(fs.WatchSetConfig{
			Path: packages,
			Validator: func(base string, info os.FileInfo) bool {
				if strings.Contains(base, ".git") {
					return false
				}

				if strings.Contains(base, binDir) || base == binDir {
					return false
				}

				if strings.Contains(base, binfile) || base == binfile {
					return false
				}

				if info != nil && info.IsDir() {
					return true
				}

				if filepath.Ext(base) != ".go" {
					return false
				}

				// log.Printf("allowed: %s", base)
				return true
			},
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> Client:File as changed: %+s\n", ev.String())
			}
		}), true)

		watcher.Bind(jsbuild, true)

		jsbuild.Send(true)

		flux.GoDefer("jsWatchBuild:kill", func() {
			<-c
			//close our builders
			watcher.Close()
			jsbuild.Close()
		})

	})

}

func addGoFriday(pm *PluginManager) {
	pm.Add("goFriday", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format

		      tag: gofriday
		      config:
		        markdown: ./markdown
		        templates: ./templates

		  		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		markdownDir := options.Config["markdown"]
		templateDir := options.Config["templates"]

		//optional args
		ext := options.Config["ext"]
		//must be a bool
		sanitizeString := options.Config["sanitize"]

		var sanitize bool

		if svz, err := strconv.ParseBool(sanitizeString); err == nil {
			sanitize = svz
		}

		if markdownDir == "" || templateDir == "" {
			fmt.Println("---> gofriday.error: expected to find keys (markdown and templates) in config map")
			return
		}

		//get the absolute path
		absDir := filepath.Join(pwd, markdownDir)
		tbsDir := filepath.Join(pwd, templateDir)

		gofriday, err := builders.GoFridayStream(builders.MarkStreamConfig{
			InputDir: absDir,
			SaveDir:  tbsDir,
			Ext:      ext,
			Sanitize: sanitize,
		})

		if err != nil {
			fmt.Printf("---> gofriday.error: %s", err)
			return
		}

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> goFriday:File as changed: %+s\n", ev.String())
			}
		}), true)
		// create the command runner set to run the args
		watcher.Bind(gofriday, true)

		flux.GoDefer("goFiday:kill", func() {
			<-c
			watcher.Close()
		})
	})
}

func addGoStaticBundle(pm *PluginManager) {
	pm.Add("goStatic", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format: you can control all aspects of the assets.BindFS using the following

		      tag: gostatic
					# add commands to run on file changes
					args:
						- touch ./templates/smirf.go
		      config:
		        in: ./markdown
		        out: ./templates
						package: smirf
						file: smirf
						gzipped: true
						nodecompression: true
						production: true // generally you want to leave this to the cli to set

		  		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		inDir := options.Config["in"]
		outDir := options.Config["out"]
		packageName := options.Config["package"]
		fileName := options.Config["file"]
		absDir := filepath.Join(pwd, inDir)
		absFile := filepath.Join(pwd, outDir, fileName+".go")

		if inDir == "" || outDir == "" || packageName == "" || fileName == "" {
			fmt.Println("---> goStatic.error: the following keys(in,out,package,file) must not be empty")
			return
		}

		//set up the boolean values
		var prod bool
		var gzip bool
		var nodcom bool
		var err error

		if gz, err := strconv.ParseBool(options.Config["gzipped"]); err == nil {
			gzip = gz
		} else {
			if config.Mode > 0 {
				gzip = true
			}
		}

		if br, err := strconv.ParseBool(options.Config["nodecompression"]); err == nil {
			nodcom = br
		}

		if pr, err := strconv.ParseBool(options.Config["production"]); err == nil {
			prod = pr
		} else {
			if config.Mode <= 0 {
				prod = false
			} else {
				prod = true
			}
		}

		gostatic, err := builders.BundleAssets(&assets.BindFSConfig{
			InDir:           inDir,
			OutDir:          outDir,
			Package:         packageName,
			File:            fileName,
			Gzipped:         gzip,
			NoDecompression: nodcom,
			Production:      prod,
		})

		if err != nil {
			fmt.Printf("---> goStatic.error: %s", err)
			return
		}

		//bundle up the assets for the main time
		gostatic.Send(true)

		var command []string

		// if runtime.GOOS == "windows" {
		// command = append(command, fmt.Sprintf("copy /b %s +,,", absFile))
		// command = append(command, fmt.Sprintf("powershell  (ls %s).LastWriteTime = Get-Date", absFile))
		// } else {
		command = append(command, fmt.Sprintf("touch %s", absFile))
		// }

		//add the args from the options
		command = append(command, options.Args...)
		// log.Printf("command %s", command)

		//adds a CommandLauncher to touch the output file to force a file change notification
		touchCommand := builders.CommandLauncher(command)
		gostatic.Bind(touchCommand, true)

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		// create the command runner set to run the args
		watcher.Bind(gostatic, true)

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> goStatic:File as changed: %+s\n", ev.String())
			}
		}), true)

		flux.GoDefer("goStatic:kill", func() {
			<-c
			gostatic.Close()
		})
	})
}

func addCommander(pm *PluginManager) {
	pm.Add("commandWatch", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format

		  tag: dirWatch
		  config:
		    path: "./static/less"
		  args:
		    - lessc ./static/less/main.less ./static/css/main.css
		    - lessc ./static/less/svg.less ./static/css/svg.css

		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		dir := options.Config["path"]

		//get the command we should run on change
		commands := options.Args

		if dir == "" {
			fmt.Printf("---> dirWatch.error: no path set in config map for plug")
			return
		}

		//get the absolute path
		absDir := filepath.Join(pwd, dir)

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> commandWatch:File as changed: %+s\n", ev.String())
			}
		}), true)
		// create the command runner set to run the args
		watcher.Bind(builders.CommandLauncher(commands), true)

		flux.GoDefer("CommandWatch:kill", func() {
			<-c
			watcher.Close()
		})
	})
}
<|endoftext|>


<commit_before>package docker

import (
	"archive/tar"
	"bytes"
	"errors"
	"github.com/dotcloud/docker/utils"
	"io"
	"io/ioutil"
	"os"
	"path"
	"testing"
	"time"
)

func TestInit(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	// Root should exist
	if _, err := os.Stat(graph.Root); err != nil {
		t.Fatal(err)
	}
	// Map() should be empty
	if l, err := graph.Map(); err != nil {
		t.Fatal(err)
	} else if len(l) != 0 {
		t.Fatalf("len(Map()) should return %d, not %d", 0, len(l))
	}
}

// Test that Register can be interrupted cleanly without side effects
func TestInterruptedRegister(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
	image := &Image{
		ID:      GenerateID(),
		Comment: "testing",
		Created: time.Now(),
	}
	go graph.Register(nil, badArchive, image)
	time.Sleep(200 * time.Millisecond)
	w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
	if _, err := graph.Get(image.ID); err == nil {
		t.Fatal("Image should not exist after Register is interrupted")
	}
	// Registering the same image again should succeed if the first register was interrupted
	goodArchive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	if err := graph.Register(nil, goodArchive, image); err != nil {
		t.Fatal(err)
	}
}

// FIXME: Do more extensive tests (ex: create multiple, delete, recreate;
//       create multiple, check the amount of images and paths, etc..)
func TestGraphCreate(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	image, err := graph.Create(archive, nil, "Testing", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := ValidateID(image.ID); err != nil {
		t.Fatal(err)
	}
	if image.Comment != "Testing" {
		t.Fatalf("Wrong comment: should be '%s', not '%s'", "Testing", image.Comment)
	}
	if image.DockerVersion != VERSION {
		t.Fatalf("Wrong docker_version: should be '%s', not '%s'", VERSION, image.DockerVersion)
	}
	images, err := graph.Map()
	if err != nil {
		t.Fatal(err)
	} else if l := len(images); l != 1 {
		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
	}
	if images[image.ID] == nil {
		t.Fatalf("Could not find image with id %s", image.ID)
	}
}

func TestRegister(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	image := &Image{
		ID:      GenerateID(),
		Comment: "testing",
		Created: time.Now(),
	}
	err = graph.Register(nil, archive, image)
	if err != nil {
		t.Fatal(err)
	}
	if images, err := graph.Map(); err != nil {
		t.Fatal(err)
	} else if l := len(images); l != 1 {
		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
	}
	if resultImg, err := graph.Get(image.ID); err != nil {
		t.Fatal(err)
	} else {
		if resultImg.ID != image.ID {
			t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
		}
		if resultImg.Comment != image.Comment {
			t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
		}
	}
}

func TestMount(t *testing.T) {
	runtime := mkRuntime(t)
	defer nuke(runtime)

	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	image, err := graph.Create(archive, nil, "Testing", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	tmp, err := ioutil.TempDir("", "docker-test-graph-mount-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmp)
	rootfs := path.Join(tmp, "rootfs")
	if err := os.MkdirAll(rootfs, 0700); err != nil {
		t.Fatal(err)
	}
	rw := path.Join(tmp, "rw")
	if err := os.MkdirAll(rw, 0700); err != nil {
		t.Fatal(err)
	}
	if err := image.Mount(runtime, rootfs, rw, "testing"); err != nil {
		t.Fatal(err)
	}
	// FIXME: test for mount contents
	defer func() {
		if err := Unmount(rootfs); err != nil {
			t.Error(err)
		}
	}()
}

// Test that an image can be deleted by its shorthand prefix
func TestDeletePrefix(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	img := createTestImage(graph, t)
	if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 0)
}

func createTestImage(graph *Graph, t *testing.T) *Image {
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	img, err := graph.Create(archive, nil, "Test image", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	return img
}

func TestDelete(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 0)
	img, err := graph.Create(archive, nil, "Bla bla", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 1)
	if err := graph.Delete(img.ID); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 0)

	archive, err = fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	// Test 2 create (same name) / 1 delete
	img1, err := graph.Create(archive, nil, "Testing", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	archive, err = fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 2)
	if err := graph.Delete(img1.ID); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 1)

	// Test delete wrong name
	if err := graph.Delete("Not_foo"); err == nil {
		t.Fatalf("Deleting wrong ID should return an error")
	}
	assertNImages(graph, t, 1)

	archive, err = fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	// Test delete twice (pull -> rm -> pull -> rm)
	if err := graph.Register(nil, archive, img1); err != nil {
		t.Fatal(err)
	}
	if err := graph.Delete(img1.ID); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 1)
}

func TestByParent(t *testing.T) {
	archive1, _ := fakeTar()
	archive2, _ := fakeTar()
	archive3, _ := fakeTar()

	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	parentImage := &Image{
		ID:      GenerateID(),
		Comment: "parent",
		Created: time.Now(),
		Parent:  "",
	}
	childImage1 := &Image{
		ID:      GenerateID(),
		Comment: "child1",
		Created: time.Now(),
		Parent:  parentImage.ID,
	}
	childImage2 := &Image{
		ID:      GenerateID(),
		Comment: "child2",
		Created: time.Now(),
		Parent:  parentImage.ID,
	}
	_ = graph.Register(nil, archive1, parentImage)
	_ = graph.Register(nil, archive2, childImage1)
	_ = graph.Register(nil, archive3, childImage2)

	byParent, err := graph.ByParent()
	if err != nil {
		t.Fatal(err)
	}
	numChildren := len(byParent[parentImage.ID])
	if numChildren != 2 {
		t.Fatalf("Expected 2 children, found %d", numChildren)
	}
}

func assertNImages(graph *Graph, t *testing.T, n int) {
	if images, err := graph.Map(); err != nil {
		t.Fatal(err)
	} else if actualN := len(images); actualN != n {
		t.Fatalf("Expected %d images, found %d", n, actualN)
	}
}

/*
 * HELPER FUNCTIONS
 */

func tempGraph(t *testing.T) *Graph {
	tmp, err := ioutil.TempDir("", "docker-graph-")
	if err != nil {
		t.Fatal(err)
	}
	graph, err := NewGraph(tmp)
	if err != nil {
		t.Fatal(err)
	}
	return graph
}

func testArchive(t *testing.T) Archive {
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	return archive
}

func fakeTar() (io.Reader, error) {
	content := []byte("Hello world!\n")
	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)
	for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
		hdr := new(tar.Header)
		hdr.Size = int64(len(content))
		hdr.Name = name
		if err := tw.WriteHeader(hdr); err != nil {
			return nil, err
		}
		tw.Write([]byte(content))
	}
	tw.Close()
	return buf, nil
}
<commit_msg>graph test: Unmount image via image.Unmount()<commit_after>package docker

import (
	"archive/tar"
	"bytes"
	"errors"
	"github.com/dotcloud/docker/utils"
	"io"
	"io/ioutil"
	"os"
	"path"
	"testing"
	"time"
)

func TestInit(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	// Root should exist
	if _, err := os.Stat(graph.Root); err != nil {
		t.Fatal(err)
	}
	// Map() should be empty
	if l, err := graph.Map(); err != nil {
		t.Fatal(err)
	} else if len(l) != 0 {
		t.Fatalf("len(Map()) should return %d, not %d", 0, len(l))
	}
}

// Test that Register can be interrupted cleanly without side effects
func TestInterruptedRegister(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
	image := &Image{
		ID:      GenerateID(),
		Comment: "testing",
		Created: time.Now(),
	}
	go graph.Register(nil, badArchive, image)
	time.Sleep(200 * time.Millisecond)
	w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
	if _, err := graph.Get(image.ID); err == nil {
		t.Fatal("Image should not exist after Register is interrupted")
	}
	// Registering the same image again should succeed if the first register was interrupted
	goodArchive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	if err := graph.Register(nil, goodArchive, image); err != nil {
		t.Fatal(err)
	}
}

// FIXME: Do more extensive tests (ex: create multiple, delete, recreate;
//       create multiple, check the amount of images and paths, etc..)
func TestGraphCreate(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	image, err := graph.Create(archive, nil, "Testing", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := ValidateID(image.ID); err != nil {
		t.Fatal(err)
	}
	if image.Comment != "Testing" {
		t.Fatalf("Wrong comment: should be '%s', not '%s'", "Testing", image.Comment)
	}
	if image.DockerVersion != VERSION {
		t.Fatalf("Wrong docker_version: should be '%s', not '%s'", VERSION, image.DockerVersion)
	}
	images, err := graph.Map()
	if err != nil {
		t.Fatal(err)
	} else if l := len(images); l != 1 {
		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
	}
	if images[image.ID] == nil {
		t.Fatalf("Could not find image with id %s", image.ID)
	}
}

func TestRegister(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	image := &Image{
		ID:      GenerateID(),
		Comment: "testing",
		Created: time.Now(),
	}
	err = graph.Register(nil, archive, image)
	if err != nil {
		t.Fatal(err)
	}
	if images, err := graph.Map(); err != nil {
		t.Fatal(err)
	} else if l := len(images); l != 1 {
		t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
	}
	if resultImg, err := graph.Get(image.ID); err != nil {
		t.Fatal(err)
	} else {
		if resultImg.ID != image.ID {
			t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
		}
		if resultImg.Comment != image.Comment {
			t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
		}
	}
}

func TestMount(t *testing.T) {
	runtime := mkRuntime(t)
	defer nuke(runtime)

	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	image, err := graph.Create(archive, nil, "Testing", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	tmp, err := ioutil.TempDir("", "docker-test-graph-mount-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmp)
	rootfs := path.Join(tmp, "rootfs")
	if err := os.MkdirAll(rootfs, 0700); err != nil {
		t.Fatal(err)
	}
	rw := path.Join(tmp, "rw")
	if err := os.MkdirAll(rw, 0700); err != nil {
		t.Fatal(err)
	}
	if err := image.Mount(runtime, rootfs, rw, "testing"); err != nil {
		t.Fatal(err)
	}
	// FIXME: test for mount contents
	defer func() {
		if err := image.Unmount(runtime, rootfs, "testing"); err != nil {
			t.Error(err)
		}
	}()
}

// Test that an image can be deleted by its shorthand prefix
func TestDeletePrefix(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	img := createTestImage(graph, t)
	if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 0)
}

func createTestImage(graph *Graph, t *testing.T) *Image {
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	img, err := graph.Create(archive, nil, "Test image", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	return img
}

func TestDelete(t *testing.T) {
	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 0)
	img, err := graph.Create(archive, nil, "Bla bla", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 1)
	if err := graph.Delete(img.ID); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 0)

	archive, err = fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	// Test 2 create (same name) / 1 delete
	img1, err := graph.Create(archive, nil, "Testing", "", nil)
	if err != nil {
		t.Fatal(err)
	}
	archive, err = fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 2)
	if err := graph.Delete(img1.ID); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 1)

	// Test delete wrong name
	if err := graph.Delete("Not_foo"); err == nil {
		t.Fatalf("Deleting wrong ID should return an error")
	}
	assertNImages(graph, t, 1)

	archive, err = fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	// Test delete twice (pull -> rm -> pull -> rm)
	if err := graph.Register(nil, archive, img1); err != nil {
		t.Fatal(err)
	}
	if err := graph.Delete(img1.ID); err != nil {
		t.Fatal(err)
	}
	assertNImages(graph, t, 1)
}

func TestByParent(t *testing.T) {
	archive1, _ := fakeTar()
	archive2, _ := fakeTar()
	archive3, _ := fakeTar()

	graph := tempGraph(t)
	defer os.RemoveAll(graph.Root)
	parentImage := &Image{
		ID:      GenerateID(),
		Comment: "parent",
		Created: time.Now(),
		Parent:  "",
	}
	childImage1 := &Image{
		ID:      GenerateID(),
		Comment: "child1",
		Created: time.Now(),
		Parent:  parentImage.ID,
	}
	childImage2 := &Image{
		ID:      GenerateID(),
		Comment: "child2",
		Created: time.Now(),
		Parent:  parentImage.ID,
	}
	_ = graph.Register(nil, archive1, parentImage)
	_ = graph.Register(nil, archive2, childImage1)
	_ = graph.Register(nil, archive3, childImage2)

	byParent, err := graph.ByParent()
	if err != nil {
		t.Fatal(err)
	}
	numChildren := len(byParent[parentImage.ID])
	if numChildren != 2 {
		t.Fatalf("Expected 2 children, found %d", numChildren)
	}
}

func assertNImages(graph *Graph, t *testing.T, n int) {
	if images, err := graph.Map(); err != nil {
		t.Fatal(err)
	} else if actualN := len(images); actualN != n {
		t.Fatalf("Expected %d images, found %d", n, actualN)
	}
}

/*
 * HELPER FUNCTIONS
 */

func tempGraph(t *testing.T) *Graph {
	tmp, err := ioutil.TempDir("", "docker-graph-")
	if err != nil {
		t.Fatal(err)
	}
	graph, err := NewGraph(tmp)
	if err != nil {
		t.Fatal(err)
	}
	return graph
}

func testArchive(t *testing.T) Archive {
	archive, err := fakeTar()
	if err != nil {
		t.Fatal(err)
	}
	return archive
}

func fakeTar() (io.Reader, error) {
	content := []byte("Hello world!\n")
	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)
	for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
		hdr := new(tar.Header)
		hdr.Size = int64(len(content))
		hdr.Name = name
		if err := tw.WriteHeader(hdr); err != nil {
			return nil, err
		}
		tw.Write([]byte(content))
	}
	tw.Close()
	return buf, nil
}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"crypto/md5"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/akutz/gofig"
	"github.com/akutz/goof"
	"github.com/akutz/gotil"

	apiclient "github.com/emccode/libstorage/api/client"
	"github.com/emccode/libstorage/api/types"
	"github.com/emccode/libstorage/api/types/context"
	apihttp "github.com/emccode/libstorage/api/types/http"
	"github.com/emccode/libstorage/api/utils"
	apiconfig "github.com/emccode/libstorage/api/utils/config"
	"github.com/emccode/libstorage/api/utils/paths"
	"github.com/emccode/libstorage/cli/executors"

	// load the drivers
	_ "github.com/emccode/libstorage/drivers/os"
)

func init() {
	registerConfig()
}

const (
	clientScope          = "libstorage.client"
	hostKey              = "libstorage.host"
	logEnabledKey        = "libstorage.client.http.logging.enabled"
	logOutKey            = "libstorage.client.http.logging.out"
	logErrKey            = "libstorage.client.http.logging.err"
	logRequestsKey       = "libstorage.client.http.logging.logrequest"
	logResponsesKey      = "libstorage.client.http.logging.logresponse"
	disableKeepAlivesKey = "libstorage.client.http.disableKeepAlives"
	lsxOffline           = "libstorage.client.executor.offline"

	// LSXPathKey is the configuration key for the libStorage executor
	// binary path.
	LSXPathKey = "libstorage.client.executor.path"
)

type lsc struct {
	apiclient.Client
	config             gofig.Config
	svcInfo            apihttp.ServicesMap
	lsxInfo            apihttp.ExecutorsMap
	lsxBinPath         string
	ctx                context.Context
	enableIIDHeader    bool
	enableLclDevHeader bool
}

// New returns a new Client.
func New(config gofig.Config) (Client, error) {

	logFields := log.Fields{}

	if config == nil {
		var err error
		if config, err = apiconfig.NewConfig(); err != nil {
			return nil, err
		}
	}

	addr := config.GetString(hostKey)
	ctx := context.WithContextID(context.Background(), "host", addr)

	proto, lAddr, err := gotil.ParseAddress(addr)
	if err != nil {
		return nil, err
	}

	tlsConfig, err := utils.ParseTLSConfig(
		config.Scope(clientScope), logFields)
	if err != nil {
		return nil, err
	}

	c := &lsc{
		Client: apiclient.Client{
			Host:         getHost(proto, lAddr, tlsConfig),
			Headers:      http.Header{},
			LogRequests:  config.GetBool(logRequestsKey),
			LogResponses: config.GetBool(logResponsesKey),
			Client: &http.Client{
				Transport: &http.Transport{
					Dial: func(string, string) (net.Conn, error) {
						if tlsConfig == nil {
							return net.Dial(proto, lAddr)
						}
						return tls.Dial(proto, lAddr, tlsConfig)
					},
					DisableKeepAlives: config.GetBool(disableKeepAlivesKey),
				},
			},
		},
		config:     config,
		ctx:        ctx,
		lsxBinPath: config.GetString(LSXPathKey),
	}

	if err := c.updateServiceInfo(); err != nil {
		return nil, err
	}

	if !config.GetBool(lsxOffline) {
		if err := c.updateExecutorInfo(); err != nil {
			return nil, err
		}

		if err := c.updateExecutor(); err != nil {
			return nil, err
		}
	}

	if err := c.updateInstanceIDs(); err != nil {
		return nil, err
	}

	if err := c.updateLocalDevices(); err != nil {
		return nil, err
	}

	ctx.Log().WithFields(logFields).Debug("created new libStorage client")

	return c, nil
}

func (c *lsc) API() *apiclient.Client {
	return &c.Client
}

func getHost(proto, lAddr string, tlsConfig *tls.Config) string {
	if tlsConfig != nil && tlsConfig.ServerName != "" {
		return tlsConfig.ServerName
	} else if proto == "unix" {
		return "libstorage-server"
	} else {
		return lAddr
	}
}

func (c *lsc) updateServiceInfo() error {
	c.ctx.Log().Debug("getting service information")
	svcInfo, err := c.Client.Services(c.ctx)
	if err != nil {
		return err
	}
	c.svcInfo = svcInfo
	return nil
}

func (c *lsc) updateExecutorInfo() error {
	c.ctx.Log().Debug("getting executor information")
	lsxInfo, err := c.Client.Executors(c.ctx)
	if err != nil {
		return err
	}
	c.lsxInfo = lsxInfo
	return nil
}

var (
	lsxBinLock = &sync.Mutex{}
)

func (c *lsc) updateExecutor() error {
	lsxi, ok := c.lsxInfo[executors.LSX]
	if !ok {
		return goof.WithField("lsx", executors.LSX, "unknown executor")
	}

	lsxBinLock.Lock()
	defer lsxBinLock.Unlock()

	if !gotil.FileExists(c.lsxBinPath) {
		return c.downloadExecutor()
	}

	checksum, err := c.getExecutorChecksum()
	if err != nil {
		return err
	}

	if lsxi.MD5Checksum != checksum {
		return c.downloadExecutor()
	}

	return nil
}

func (c *lsc) getExecutorChecksum() (string, error) {
	f, err := os.Open(c.lsxBinPath)
	if err != nil {
		return "", err
	}
	defer f.Close()

	h := md5.New()
	buf := make([]byte, 1024)
	for {
		n, err := f.Read(buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return "", err
		}
		if _, err := h.Write(buf[:n]); err != nil {
			return "", err
		}
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func (c *lsc) downloadExecutor() error {

	if err := func() error {
		f, err := os.Create(c.lsxBinPath)
		if err != nil {
			return err
		}
		defer f.Close()

		rdr, err := c.Client.ExecutorGet(c.ctx, executors.LSX)
		if _, err := io.Copy(f, rdr); err != nil {
			return err
		}

		return nil
	}(); err != nil {
		return err
	}

	if err := os.Chmod(c.lsxBinPath, 0755); err != nil {
		return err
	}

	return nil
}

type iidHeader struct {
	driverName string
	headerName string
	headerValu string
}

func (c *lsc) updateInstanceIDs() error {
	if !EnableInstanceIDHeaders {
		return nil
	}

	c.ctx.Log().Debug("getting instance IDs")

	cache := map[string]*iidHeader{}

	for service, si := range c.svcInfo {

		if _, ok := cache[si.Driver.Name]; ok {
			continue
		}

		iid, err := c.InstanceID(service)
		if err != nil {
			return err
		}

		var h *iidHeader

		if len(iid.Metadata) == 0 {
			h = &iidHeader{
				headerName: apihttp.InstanceIDHeader,
				headerValu: iid.ID,
			}
		} else {
			jBuf, err := json.Marshal(iid)
			if err != nil {
				return err
			}
			h = &iidHeader{
				headerName: apihttp.InstanceID64Header,
				headerValu: base64.StdEncoding.EncodeToString(jBuf),
			}
		}

		h.driverName = si.Driver.Name
		cache[h.driverName] = h
	}

	for _, h := range cache {
		c.Client.Headers.Add(
			h.headerName,
			fmt.Sprintf("%s=%s", h.driverName, h.headerValu))
	}

	return nil
}

type ldHeader struct {
	driverName string
	headerName string
	headerValu map[string]string
}

func (c *lsc) updateLocalDevices() error {
	if !EnableLocalDevicesHeaders {
		return nil
	}

	c.ctx.Log().Debug("getting local devices")

	cache := map[string]*ldHeader{}

	for service, si := range c.svcInfo {

		if _, ok := cache[si.Driver.Name]; ok {
			continue
		}

		ldm, err := c.LocalDevices(service)
		if err != nil {
			return err
		}

		h := &ldHeader{
			driverName: si.Driver.Name,
			headerName: apihttp.LocalDevicesHeader,
			headerValu: ldm,
		}

		cache[h.driverName] = h
	}

	for _, h := range cache {
		buf := &bytes.Buffer{}

		fmt.Fprintf(buf, "%s=", h.driverName)
		for device, mountPoint := range h.headerValu {
			fmt.Fprintf(buf, "%s=%s, ", device, mountPoint)
		}

		if buf.Len() > (len(h.driverName) + 1) {
			buf.Truncate(buf.Len() - 2)
		}

		c.Client.Headers.Add(h.headerName, buf.String())
	}

	return nil
}

func (c *lsc) getServiceInfo(service string) (*types.ServiceInfo, error) {
	si, ok := c.svcInfo[strings.ToLower(service)]
	if !ok {
		return nil, goof.WithField("name", service, "unknown service")
	}
	return si, nil
}

func registerConfig() {
	r := gofig.NewRegistration("libStorage Client")
	lsxBinPath := fmt.Sprintf("%s/%s", paths.UsrDirPath(), executors.LSX)
	r.Key(gofig.String, "", lsxBinPath, "", LSXPathKey)
	r.Key(gofig.Bool, "", false, "", lsxOffline)
	r.Key(gofig.Bool, "", false, "", logEnabledKey)
	r.Key(gofig.String, "", "", "", logOutKey)
	r.Key(gofig.String, "", "", "", logErrKey)
	r.Key(gofig.Bool, "", false, "", logRequestsKey)
	r.Key(gofig.Bool, "", false, "", logResponsesKey)
	r.Key(gofig.Bool, "", false, "", disableKeepAlivesKey)
	gofig.Register(r)
}
<commit_msg>LSXBin Chmod Text File Busy Fix - Part 2<commit_after>package client

import (
	"bytes"
	"crypto/md5"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/akutz/gofig"
	"github.com/akutz/goof"
	"github.com/akutz/gotil"

	apiclient "github.com/emccode/libstorage/api/client"
	"github.com/emccode/libstorage/api/types"
	"github.com/emccode/libstorage/api/types/context"
	apihttp "github.com/emccode/libstorage/api/types/http"
	"github.com/emccode/libstorage/api/utils"
	apiconfig "github.com/emccode/libstorage/api/utils/config"
	"github.com/emccode/libstorage/api/utils/paths"
	"github.com/emccode/libstorage/cli/executors"

	// load the drivers
	_ "github.com/emccode/libstorage/drivers/os"
)

func init() {
	registerConfig()
}

const (
	clientScope          = "libstorage.client"
	hostKey              = "libstorage.host"
	logEnabledKey        = "libstorage.client.http.logging.enabled"
	logOutKey            = "libstorage.client.http.logging.out"
	logErrKey            = "libstorage.client.http.logging.err"
	logRequestsKey       = "libstorage.client.http.logging.logrequest"
	logResponsesKey      = "libstorage.client.http.logging.logresponse"
	disableKeepAlivesKey = "libstorage.client.http.disableKeepAlives"
	lsxOffline           = "libstorage.client.executor.offline"

	// LSXPathKey is the configuration key for the libStorage executor
	// binary path.
	LSXPathKey = "libstorage.client.executor.path"
)

type lsc struct {
	apiclient.Client
	config             gofig.Config
	svcInfo            apihttp.ServicesMap
	lsxInfo            apihttp.ExecutorsMap
	lsxBinPath         string
	ctx                context.Context
	enableIIDHeader    bool
	enableLclDevHeader bool
}

// New returns a new Client.
func New(config gofig.Config) (Client, error) {

	logFields := log.Fields{}

	if config == nil {
		var err error
		if config, err = apiconfig.NewConfig(); err != nil {
			return nil, err
		}
	}

	addr := config.GetString(hostKey)
	ctx := context.WithContextID(context.Background(), "host", addr)

	proto, lAddr, err := gotil.ParseAddress(addr)
	if err != nil {
		return nil, err
	}

	tlsConfig, err := utils.ParseTLSConfig(
		config.Scope(clientScope), logFields)
	if err != nil {
		return nil, err
	}

	c := &lsc{
		Client: apiclient.Client{
			Host:         getHost(proto, lAddr, tlsConfig),
			Headers:      http.Header{},
			LogRequests:  config.GetBool(logRequestsKey),
			LogResponses: config.GetBool(logResponsesKey),
			Client: &http.Client{
				Transport: &http.Transport{
					Dial: func(string, string) (net.Conn, error) {
						if tlsConfig == nil {
							return net.Dial(proto, lAddr)
						}
						return tls.Dial(proto, lAddr, tlsConfig)
					},
					DisableKeepAlives: config.GetBool(disableKeepAlivesKey),
				},
			},
		},
		config:     config,
		ctx:        ctx,
		lsxBinPath: config.GetString(LSXPathKey),
	}

	if err := c.updateServiceInfo(); err != nil {
		return nil, err
	}

	if !config.GetBool(lsxOffline) {
		if err := c.updateExecutorInfo(); err != nil {
			return nil, err
		}

		if err := c.updateExecutor(); err != nil {
			return nil, err
		}
	}

	if err := c.updateInstanceIDs(); err != nil {
		return nil, err
	}

	if err := c.updateLocalDevices(); err != nil {
		return nil, err
	}

	ctx.Log().WithFields(logFields).Debug("created new libStorage client")

	return c, nil
}

func (c *lsc) API() *apiclient.Client {
	return &c.Client
}

func getHost(proto, lAddr string, tlsConfig *tls.Config) string {
	if tlsConfig != nil && tlsConfig.ServerName != "" {
		return tlsConfig.ServerName
	} else if proto == "unix" {
		return "libstorage-server"
	} else {
		return lAddr
	}
}

func (c *lsc) updateServiceInfo() error {
	c.ctx.Log().Debug("getting service information")
	svcInfo, err := c.Client.Services(c.ctx)
	if err != nil {
		return err
	}
	c.svcInfo = svcInfo
	return nil
}

func (c *lsc) updateExecutorInfo() error {
	c.ctx.Log().Debug("getting executor information")
	lsxInfo, err := c.Client.Executors(c.ctx)
	if err != nil {
		return err
	}
	c.lsxInfo = lsxInfo
	return nil
}

var (
	lsxBinLock = &sync.Mutex{}
)

func (c *lsc) updateExecutor() error {
	lsxi, ok := c.lsxInfo[executors.LSX]
	if !ok {
		return goof.WithField("lsx", executors.LSX, "unknown executor")
	}

	lsxBinLock.Lock()
	defer lsxBinLock.Unlock()

	if !gotil.FileExists(c.lsxBinPath) {
		return c.downloadExecutor()
	}

	checksum, err := c.getExecutorChecksum()
	if err != nil {
		return err
	}

	if lsxi.MD5Checksum != checksum {
		return c.downloadExecutor()
	}

	return nil
}

func (c *lsc) getExecutorChecksum() (string, error) {
	f, err := os.Open(c.lsxBinPath)
	if err != nil {
		return "", err
	}
	defer f.Close()

	h := md5.New()
	buf := make([]byte, 1024)
	for {
		n, err := f.Read(buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return "", err
		}
		if _, err := h.Write(buf[:n]); err != nil {
			return "", err
		}
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func (c *lsc) downloadExecutor() error {

	f, err := os.OpenFile(
		c.lsxBinPath,
		os.O_CREATE|os.O_RDWR|os.O_TRUNC,
		0755)
	if err != nil {
		return err
	}

	defer f.Close()

	rdr, err := c.Client.ExecutorGet(c.ctx, executors.LSX)
	if _, err := io.Copy(f, rdr); err != nil {
		return err
	}

	return nil
}

type iidHeader struct {
	driverName string
	headerName string
	headerValu string
}

func (c *lsc) updateInstanceIDs() error {
	if !EnableInstanceIDHeaders {
		return nil
	}

	c.ctx.Log().Debug("getting instance IDs")

	cache := map[string]*iidHeader{}

	for service, si := range c.svcInfo {

		if _, ok := cache[si.Driver.Name]; ok {
			continue
		}

		iid, err := c.InstanceID(service)
		if err != nil {
			return err
		}

		var h *iidHeader

		if len(iid.Metadata) == 0 {
			h = &iidHeader{
				headerName: apihttp.InstanceIDHeader,
				headerValu: iid.ID,
			}
		} else {
			jBuf, err := json.Marshal(iid)
			if err != nil {
				return err
			}
			h = &iidHeader{
				headerName: apihttp.InstanceID64Header,
				headerValu: base64.StdEncoding.EncodeToString(jBuf),
			}
		}

		h.driverName = si.Driver.Name
		cache[h.driverName] = h
	}

	for _, h := range cache {
		c.Client.Headers.Add(
			h.headerName,
			fmt.Sprintf("%s=%s", h.driverName, h.headerValu))
	}

	return nil
}

type ldHeader struct {
	driverName string
	headerName string
	headerValu map[string]string
}

func (c *lsc) updateLocalDevices() error {
	if !EnableLocalDevicesHeaders {
		return nil
	}

	c.ctx.Log().Debug("getting local devices")

	cache := map[string]*ldHeader{}

	for service, si := range c.svcInfo {

		if _, ok := cache[si.Driver.Name]; ok {
			continue
		}

		ldm, err := c.LocalDevices(service)
		if err != nil {
			return err
		}

		h := &ldHeader{
			driverName: si.Driver.Name,
			headerName: apihttp.LocalDevicesHeader,
			headerValu: ldm,
		}

		cache[h.driverName] = h
	}

	for _, h := range cache {
		buf := &bytes.Buffer{}

		fmt.Fprintf(buf, "%s=", h.driverName)
		for device, mountPoint := range h.headerValu {
			fmt.Fprintf(buf, "%s=%s, ", device, mountPoint)
		}

		if buf.Len() > (len(h.driverName) + 1) {
			buf.Truncate(buf.Len() - 2)
		}

		c.Client.Headers.Add(h.headerName, buf.String())
	}

	return nil
}

func (c *lsc) getServiceInfo(service string) (*types.ServiceInfo, error) {
	si, ok := c.svcInfo[strings.ToLower(service)]
	if !ok {
		return nil, goof.WithField("name", service, "unknown service")
	}
	return si, nil
}

func registerConfig() {
	r := gofig.NewRegistration("libStorage Client")
	lsxBinPath := fmt.Sprintf("%s/%s", paths.UsrDirPath(), executors.LSX)
	r.Key(gofig.String, "", lsxBinPath, "", LSXPathKey)
	r.Key(gofig.Bool, "", false, "", lsxOffline)
	r.Key(gofig.Bool, "", false, "", logEnabledKey)
	r.Key(gofig.String, "", "", "", logOutKey)
	r.Key(gofig.String, "", "", "", logErrKey)
	r.Key(gofig.Bool, "", false, "", logRequestsKey)
	r.Key(gofig.Bool, "", false, "", logResponsesKey)
	r.Key(gofig.Bool, "", false, "", disableKeepAlivesKey)
	gofig.Register(r)
}
<|endoftext|>


<commit_before>package glock

import (
	"bufio"
	"bytes"
	"fmt"
	"math/rand"
	"net"
	"os/exec"
	"strings"
	"sync"
	"testing"
	"time"
)

func TestPingPong(t *testing.T) {
	conn, err := net.Dial("tcp", "localhost:45625")
	if err != nil {
		t.Error("Unexpected connection error: ", err)
	}
	fmt.Fprintf(conn, "PING\n")
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		split := strings.Fields(scanner.Text())
		if split[0] != "PONG" {
			t.Error("Unexpected ping error: ", err)
		} else {
			break
		}
	}
}

var glockServers = []string{"glock01.iron.io:45625"} //"localhost:45625", "localhost:45626", "localhost:45627"}

func TestLockUnlock(t *testing.T) {
	client1, err := NewClient(glockServers, 10)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	fmt.Println("1 getting lock")
	id1, err := client1.Lock("x", 10*time.Second)
	if err != nil {
		t.Error("Unexpected lock error: ", err)
	}
	fmt.Println("1 got lock")

	go func() {
		fmt.Println("2 getting lock")
		id2, err := client1.Lock("x", 10*time.Second)
		if err != nil {
			t.Error("Unexpected lock error: ", err)
		}
		fmt.Println("2 got lock")

		time.Sleep(1 * time.Second)
		fmt.Println("2 releasing lock")
		err = client1.Unlock("x", id2)
		if err != nil {
			t.Error("Unexpected Unlock error: ", err)
		}
		fmt.Println("2 released lock")
	}()

	fmt.Println("sleeping")
	time.Sleep(2 * time.Second)
	fmt.Println("finished sleeping")

	fmt.Println("1 releasing lock")
	err = client1.Unlock("x", id1)
	if err != nil {
		t.Error("Unexpected Unlock error: ", err)
	}

	fmt.Println("1 released lock")

	time.Sleep(5 * time.Second)
}

func TestConnectionDrop(t *testing.T) {
	client1, err := NewClient(glockServers, 10)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	fmt.Println("closing connection")
	client1.testClose()
	fmt.Println("closed connection")

	fmt.Println("1 getting lock")
	id1, err := client1.Lock("x", 1*time.Second)
	if err != nil {
		t.Error("Unexpected lock error: ", err)
	}
	fmt.Println("1 got lock")

	fmt.Println("1 releasing lock")
	err = client1.Unlock("x", id1)
	if err != nil {
		t.Error("Unexpected Unlock error: ", err)
	}
	fmt.Println("1 released lock")

	client1.testClose()

}

// // This is used to simulate dropped out or bad connections in the connection pool
func (c *Client) testClose() {
	for server, pool := range c.connectionPools {
		fmt.Println(server)
		size := len(pool)
		for x := 0; x < size; x++ {
			connection := <-pool
			connection.Close()
			pool <- connection
		}
	}
}

func TestConcurrency(t *testing.T) {
	client1, err := NewClient(glockServers, 500)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	var wg sync.WaitGroup
	k := 'a'
	for i := 0; i < 1000; i++ {
		fmt.Println("Value of i is now:", i)
		if i > 0 && i%50 == 0 {
			k += 1
		}
		wg.Add(1)
		go func(ii int, key string) {
			defer wg.Done()
			fmt.Println("goroutine: ", ii, "getting lock", key)
			id1, err := client1.Lock(key, 1000*time.Millisecond)
			if err != nil {
				t.Error("goroutine: ", ii, "Unexpected lock error: ", err)
			}
			fmt.Println("goroutine: ", ii, "GOT LOCK", key)
			time.Sleep(time.Duration(rand.Intn(60)) * time.Millisecond)
			fmt.Println("goroutine: ", ii, "releasing lock", key)
			err = client1.Unlock(key, id1)
			if err != nil {
				fmt.Println("goroutine: ", ii, key, "Already unlocked, it's ok: ", err)
				t.Error("goroutine: ", ii, "Unexpected Unlock error: ", err)
			} else {
				fmt.Println("goroutine: ", ii, "released lock", key)
			}
			fmt.Println("pool size: ", client1.Size())
		}(i, string(k))
	}
	fmt.Println("waiting for waitgroup...")
	wg.Wait()
	fmt.Println("Done waiting for waitgroup")

}

func TestServerDrop(t *testing.T) {
	client1, err := NewClient(glockServers, 500)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	k := 'a'
	for i := 0; i < 100; i++ {
		fmt.Println("Value of i is now:", i)
		if i > 0 && i%50 == 0 {
			k += 1
		}
		key := string(k)
		fmt.Println("getting lock", key)
		id1, err := client1.Lock(key, 1000*time.Millisecond)
		fmt.Println("Returning from lock", id1, err)
		if err != nil {
			t.Error("Unexpected lock error: ", err)
		}
		fmt.Println("GOT LOCK", key)
		time.Sleep(time.Duration(rand.Intn(60)) * time.Millisecond)
		if i == 40 {
			testDropServer()
			fmt.Println("Dropping server after lock is acquired")
		}

		fmt.Println("releasing lock", key)
		err = client1.Unlock(key, id1)
		if err != nil {
			fmt.Println(key, "Already unlocked, it's ok: ", err)
			if i != 40 {
				t.Error("goroutine: ", i, "Unexpected Unlock error: ", err)
			} else {
				fmt.Println("server dropped out, safe to ignore")
			}

		} else {
			fmt.Println("released lock", key)
		}
		fmt.Println("pool size: ", client1.Size())

	}
	time.Sleep(1 * time.Second)
	fmt.Println("pool size: ", client1.Size())
}

// A little hack to simulate server dropped out
func testDropServer() {
	cmd := exec.Command("pidof", "glock")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		fmt.Println(err)
	}
	pids := strings.Split(out.String(), " ")
	if len(pids) > 1 {
		pid := pids[0]
		cmd = exec.Command("kill", "-9", pid)
		cmd.Run()
	}

}
<commit_msg>Made hosts global in tests<commit_after>package glock

import (
	"bufio"
	"bytes"
	"fmt"
	"math/rand"
	"net"
	"os/exec"
	"strings"
	"sync"
	"testing"
	"time"
)

var glockServers = []string{"localhost:45625"} //"localhost:45625", "localhost:45626", "localhost:45627"}

func TestPingPong(t *testing.T) {
	conn, err := net.Dial("tcp", glockServers[0])
	if err != nil {
		t.Error("Unexpected connection error: ", err)
	}
	fmt.Fprintf(conn, "PING\n")
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		split := strings.Fields(scanner.Text())
		if split[0] != "PONG" {
			t.Error("Unexpected ping error: ", err)
		} else {
			break
		}
	}
}

func TestLockUnlock(t *testing.T) {
	client1, err := NewClient(glockServers, 10)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	fmt.Println("1 getting lock")
	id1, err := client1.Lock("x", 10*time.Second)
	if err != nil {
		t.Error("Unexpected lock error: ", err)
	}
	fmt.Println("1 got lock")

	go func() {
		fmt.Println("2 getting lock")
		id2, err := client1.Lock("x", 10*time.Second)
		if err != nil {
			t.Error("Unexpected lock error: ", err)
		}
		fmt.Println("2 got lock")

		time.Sleep(1 * time.Second)
		fmt.Println("2 releasing lock")
		err = client1.Unlock("x", id2)
		if err != nil {
			t.Error("Unexpected Unlock error: ", err)
		}
		fmt.Println("2 released lock")
	}()

	fmt.Println("sleeping")
	time.Sleep(2 * time.Second)
	fmt.Println("finished sleeping")

	fmt.Println("1 releasing lock")
	err = client1.Unlock("x", id1)
	if err != nil {
		t.Error("Unexpected Unlock error: ", err)
	}

	fmt.Println("1 released lock")

	time.Sleep(5 * time.Second)
}

func TestConnectionDrop(t *testing.T) {
	client1, err := NewClient(glockServers, 10)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	fmt.Println("closing connection")
	client1.testClose()
	fmt.Println("closed connection")

	fmt.Println("1 getting lock")
	id1, err := client1.Lock("x", 1*time.Second)
	if err != nil {
		t.Error("Unexpected lock error: ", err)
	}
	fmt.Println("1 got lock")

	fmt.Println("1 releasing lock")
	err = client1.Unlock("x", id1)
	if err != nil {
		t.Error("Unexpected Unlock error: ", err)
	}
	fmt.Println("1 released lock")

	client1.testClose()

}

// // This is used to simulate dropped out or bad connections in the connection pool
func (c *Client) testClose() {
	for server, pool := range c.connectionPools {
		fmt.Println(server)
		size := len(pool)
		for x := 0; x < size; x++ {
			connection := <-pool
			connection.Close()
			pool <- connection
		}
	}
}

func TestConcurrency(t *testing.T) {
	client1, err := NewClient(glockServers, 500)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	var wg sync.WaitGroup
	k := 'a'
	for i := 0; i < 1000; i++ {
		fmt.Println("Value of i is now:", i)
		if i > 0 && i%50 == 0 {
			k += 1
		}
		wg.Add(1)
		go func(ii int, key string) {
			defer wg.Done()
			fmt.Println("goroutine: ", ii, "getting lock", key)
			id1, err := client1.Lock(key, 1000*time.Millisecond)
			if err != nil {
				t.Error("goroutine: ", ii, "Unexpected lock error: ", err)
			}
			fmt.Println("goroutine: ", ii, "GOT LOCK", key)
			time.Sleep(time.Duration(rand.Intn(60)) * time.Millisecond)
			fmt.Println("goroutine: ", ii, "releasing lock", key)
			err = client1.Unlock(key, id1)
			if err != nil {
				fmt.Println("goroutine: ", ii, key, "Already unlocked, it's ok: ", err)
				t.Error("goroutine: ", ii, "Unexpected Unlock error: ", err)
			} else {
				fmt.Println("goroutine: ", ii, "released lock", key)
			}
			fmt.Println("pool size: ", client1.Size())
		}(i, string(k))
	}
	fmt.Println("waiting for waitgroup...")
	wg.Wait()
	fmt.Println("Done waiting for waitgroup")

}

func TestServerDrop(t *testing.T) {
	client1, err := NewClient(glockServers, 500)
	if err != nil {
		t.Error("Unexpected new client error: ", err)
	}

	k := 'a'
	for i := 0; i < 100; i++ {
		fmt.Println("Value of i is now:", i)
		if i > 0 && i%50 == 0 {
			k += 1
		}
		key := string(k)
		fmt.Println("getting lock", key)
		id1, err := client1.Lock(key, 1000*time.Millisecond)
		fmt.Println("Returning from lock", id1, err)
		if err != nil {
			t.Error("Unexpected lock error: ", err)
		}
		fmt.Println("GOT LOCK", key)
		time.Sleep(time.Duration(rand.Intn(60)) * time.Millisecond)
		if i == 40 {
			testDropServer()
			fmt.Println("Dropping server after lock is acquired")
		}

		fmt.Println("releasing lock", key)
		err = client1.Unlock(key, id1)
		if err != nil {
			fmt.Println(key, "Already unlocked, it's ok: ", err)
			if i != 40 {
				t.Error("goroutine: ", i, "Unexpected Unlock error: ", err)
			} else {
				fmt.Println("server dropped out, safe to ignore")
			}

		} else {
			fmt.Println("released lock", key)
		}
		fmt.Println("pool size: ", client1.Size())

	}
	time.Sleep(1 * time.Second)
	fmt.Println("pool size: ", client1.Size())
}

// A little hack to simulate server dropped out
func testDropServer() {
	cmd := exec.Command("pidof", "glock")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		fmt.Println(err)
	}
	pids := strings.Split(out.String(), " ")
	if len(pids) > 1 {
		pid := pids[0]
		cmd = exec.Command("kill", "-9", pid)
		cmd.Run()
	}

}
<|endoftext|>