<commit_before>package html

import (
	"code.google.com/p/go.net/html"
	"errors"
	"github.com/slyrz/newscat/util"
	"io"
	"unicode"
)

const (
	// We remember a few special node types when descending into their
	// children.
	AncestorArticle = 1 << iota
	AncestorAside
	AncestorBlockquote
	AncestorList
)

type Document struct {
	Title  *util.Text // the <title>...</title> text.
	Chunks []*Chunk   // list of all chunks found in this document

	// Unexported fields.
	html *html.Node // the ... part
	head *html.Node // the ... part
	body *html.Node // the ... part

	// State variables used when collectiong chunks.
	ancestors int // bitmask which stores ancestor of the current node

	// Number of non-space characters inside link tags / normal tags
	// per html.ElementNode.
	linkText map[*html.Node]int // length of text inside  tags
	normText map[*html.Node]int // length of text outside  tags
}

func NewDocument(r io.Reader) (*Document, error) {
	doc := new(Document)
	doc.Chunks = make([]*Chunk, 0, 512)
	if err := doc.Parse(r); err != nil {
		return nil, err
	}
	return doc, nil
}

func (doc *Document) Parse(r io.Reader) error {
	root, err := html.Parse(r)
	if err != nil {
		return err
	}
	// Assign the fields html, head and body from the HTML page.
	doc.setNodes(root)

	// Check if ,  and  nodes were found.
	if doc.html == nil || doc.head == nil || doc.body == nil {
		return errors.New("Document missing ,  or .")
	}

	doc.parseHead(doc.head)

	// If no title was found, detecting the main heading might fail.
	if doc.Title == nil {
		doc.Title = util.NewText()
	}
	doc.linkText = make(map[*html.Node]int)
	doc.normText = make(map[*html.Node]int)

	doc.cleanBody(doc.body, 0)
	doc.countText(doc.body, false)
	doc.parseBody(doc.body)

	// Now link the chunks.
	for i := range doc.Chunks {
		if i > 0 {
			doc.Chunks[i].Prev = doc.Chunks[i-1]
		}
		if i < len(doc.Chunks)-1 {
			doc.Chunks[i].Next = doc.Chunks[i+1]
		}
	}
	return nil
}

// Assign the struct fields html, head and body from the HTML tree of node n.
//  doc.html ->
//  doc.head ->
//  doc.body ->
func (doc *Document) setNodes(n *html.Node) {
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		switch c.Data {
		case "html":
			doc.html = c
			doc.setNodes(c)
		case "body":
			doc.body = c
		case "head":
			doc.head = c
		}
	}
}

// parseHead parses the ... part of the HTML page. Right now it
// only detects the <title>...</title>.
func (doc *Document) parseHead(n *html.Node) {
	if n.Type == html.ElementNode && n.Data == "title" {
		if chunk, err := NewChunk(doc, n); err == nil {
			doc.Title = chunk.Text
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		doc.parseHead(c)
	}
}

// countText counts the link text and the normal text per html.Node.
// "Link text" is text inside  tags and "normal text" is text inside
// anything but  tags. Of course, counting is done cumulative, so the
// numbers of a parent node include the numbers of it's child nodes.
func (doc *Document) countText(n *html.Node, insideLink bool) (linkText int, normText int) {
	linkText = 0
	normText = 0
	if n.Type == html.ElementNode && n.Data == "a" {
		insideLink = true
	}
	for s := n.FirstChild; s != nil; s = s.NextSibling {
		linkTextChild, normTextChild := doc.countText(s, insideLink)
		linkText += linkTextChild
		normText += normTextChild
	}
	if n.Type == html.TextNode {
		count := 0
		for _, rune := range n.Data {
			if unicode.IsLetter(rune) {
				count += 1
			}
		}
		if insideLink {
			linkText += count
		} else {
			normText += count
		}
	}
	doc.linkText[n] = linkText
	doc.normText[n] = normText
	return
}

// cleanBody removes unwanted HTML elements from the HTML body.
func (doc *Document) cleanBody(n *html.Node, level int) {

	// removeNode returns true if a node should be removed from HTML document.
	removeNode := func(c *html.Node, level int) bool {
		switch c.Data {
		// Elements save to ignore.
		case "address", "audio", "button", "canvas", "caption", "fieldset",
			"figcaption", "figure", "footer", "form", "frame", "iframe",
			"map", "menu", "nav", "noscript", "object", "option", "output",
			"script", "select", "style", "svg", "textarea", "video":
			return true
		// High-level tables might be used to layout the document, so we better
		// not ignore them.
		case "table":
			return level > 5
		}
		return false
	}

	var curr *html.Node = n.FirstChild
	var next *html.Node = nil
	for ; curr != nil; curr = next {
		// We have to remember the next sibling here becase calling RemoveChild
		// sets curr's NextSibling pointer to nil and we would quit the loop
		// prematurely.
		next = curr.NextSibling
		if curr.Type == html.ElementNode {
			if removeNode(curr, level) {
				n.RemoveChild(curr)
			} else {
				doc.cleanBody(curr, level+1)
			}
		}
	}
}

var (
	ignoreNames = util.NewRegexFromWords(
		"breadcrumb",
		"byline",
		"caption",
		"comment",
		"community",
		"credit",
		"description",
		"email",
		"foot",
		"gallery",
		"hide",
		"photo",
		"related",
		"social",
		"story[-_]?bar",
		"story[-_]?feature",
	)
	ignoreStyle = util.NewRegex(`(?i)display:\s*none`)
)

// parseBody parses the ... part of the HTML page. It creates
// Chunks for every html.TextNode found in the body.
func (doc *Document) parseBody(n *html.Node) {
	switch n.Type {
	case html.ElementNode:
		// We ignore the node if it has some nasty classes/ids/itemprobs or if
		// it contains "display: none" in its style attribute.
		for _, attr := range n.Attr {
			switch attr.Key {
			case "id", "class", "itemprop":
				if ignoreNames.In(attr.Val) {
					return
				}
			case "style":
				if ignoreStyle.In(attr.Val) {
					return
				}
			}
		}
		ancestorMask := 0
		switch n.Data {
		// We convert headings and links to text immediately. This is easier
		// and feasible because headings and links don't contain many children.
		// Descending into these children and handling every TextNode separately
		// would make things unnecessary complicated and our results noisy.
		case "h1", "h2", "h3", "h4", "h5", "h6", "a":
			if chunk, err := NewChunk(doc, n); err == nil {
				doc.Chunks = append(doc.Chunks, chunk)
			}
			return
		// Now mask the element type, but only if it isn't already set.
		// If we mask a bit which was already set by one of our callers, we'd also
		// clear it at the end of this function, though it actually should be cleared
		// by the caller.
		case "article":
			ancestorMask = AncestorArticle &^ doc.ancestors
		case "aside":
			ancestorMask = AncestorAside &^ doc.ancestors
		case "blockquote":
			ancestorMask = AncestorBlockquote &^ doc.ancestors
		case "ul", "ol":
			ancestorMask = AncestorList &^ doc.ancestors
		}
		// Add our mask to the ancestor bitmask.
		doc.ancestors |= ancestorMask
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			doc.parseBody(c)
		}
		// Remove our mask from the ancestor bitmask.
		doc.ancestors &^= ancestorMask
	case html.TextNode:
		if chunk, err := NewChunk(doc, n); err == nil {
			doc.Chunks = append(doc.Chunks, chunk)
		}
	}
}

type TextStat struct {
	Words     int
	Sentences int
	Count     int
}

// GetClassStats groups the document chunks by their classes (defined by the
// class attribute of HTML nodes) and calculates TextStats for each class.
func (doc *Document) GetClassStats() map[string]*TextStat {
	result := make(map[string]*TextStat)
	for _, chunk := range doc.Chunks {
		for _, class := range chunk.Classes {
			if stat, ok := result[class]; ok {
				stat.Words += chunk.Text.Words
				stat.Sentences += chunk.Text.Sentences
				stat.Count += 1
			} else {
				result[class] = &TextStat{chunk.Text.Words, chunk.Text.Sentences, 1}
			}
		}
	}
	return result
}

// GetClusterStats groups the document chunks by common ancestors and
// calculates TextStats for each group of chunks.
func (doc *Document) GetClusterStats() map[*Chunk]*TextStat {
	// Don't ascend further than this constant.
	const maxAncestors = 3

	// Count TextStats for Chunk ancestors.
	ancestorStat := make(map[*html.Node]*TextStat)
	for _, chunk := range doc.Chunks {
		node, count := chunk.Block, 0
		for node != nil && count < maxAncestors {
			if stat, ok := ancestorStat[node]; ok {
				stat.Words += chunk.Text.Words
				stat.Sentences += chunk.Text.Sentences
				stat.Count += 1
			} else {
				ancestorStat[node] = &TextStat{chunk.Text.Words, chunk.Text.Sentences, 1}
			}
			node, count = node.Parent, count+1
		}
	}

	// Generate result.
	result := make(map[*Chunk]*TextStat)
	for _, chunk := range doc.Chunks {
		node := chunk.Block
		if node == nil {
			continue
		}
		// Start with the parent's TextStat. Then ascend and check if the
		// current chunk has an ancestor with better stats. Use the best stat
		// as result.
		stat := ancestorStat[node]
		for {
			if node = node.Parent; node == nil {
				break
			}
			if statPrev, ok := ancestorStat[node]; ok {
				if stat.Count < statPrev.Count {
					stat = statPrev
				}
			} else {
				break
			}
		}
		result[chunk] = stat
	}
	return result
}
<commit_msg>added keyword<commit_after>package html

import (
	"code.google.com/p/go.net/html"
	"errors"
	"github.com/slyrz/newscat/util"
	"io"
	"unicode"
)

const (
	// We remember a few special node types when descending into their
	// children.
	AncestorArticle = 1 << iota
	AncestorAside
	AncestorBlockquote
	AncestorList
)

type Document struct {
	Title  *util.Text // the <title>...</title> text.
	Chunks []*Chunk   // list of all chunks found in this document

	// Unexported fields.
	html *html.Node // the ... part
	head *html.Node // the ... part
	body *html.Node // the ... part

	// State variables used when collectiong chunks.
	ancestors int // bitmask which stores ancestor of the current node

	// Number of non-space characters inside link tags / normal tags
	// per html.ElementNode.
	linkText map[*html.Node]int // length of text inside  tags
	normText map[*html.Node]int // length of text outside  tags
}

func NewDocument(r io.Reader) (*Document, error) {
	doc := new(Document)
	doc.Chunks = make([]*Chunk, 0, 512)
	if err := doc.Parse(r); err != nil {
		return nil, err
	}
	return doc, nil
}

func (doc *Document) Parse(r io.Reader) error {
	root, err := html.Parse(r)
	if err != nil {
		return err
	}
	// Assign the fields html, head and body from the HTML page.
	doc.setNodes(root)

	// Check if ,  and  nodes were found.
	if doc.html == nil || doc.head == nil || doc.body == nil {
		return errors.New("Document missing ,  or .")
	}

	doc.parseHead(doc.head)

	// If no title was found, detecting the main heading might fail.
	if doc.Title == nil {
		doc.Title = util.NewText()
	}
	doc.linkText = make(map[*html.Node]int)
	doc.normText = make(map[*html.Node]int)

	doc.cleanBody(doc.body, 0)
	doc.countText(doc.body, false)
	doc.parseBody(doc.body)

	// Now link the chunks.
	for i := range doc.Chunks {
		if i > 0 {
			doc.Chunks[i].Prev = doc.Chunks[i-1]
		}
		if i < len(doc.Chunks)-1 {
			doc.Chunks[i].Next = doc.Chunks[i+1]
		}
	}
	return nil
}

// Assign the struct fields html, head and body from the HTML tree of node n.
//  doc.html ->
//  doc.head ->
//  doc.body ->
func (doc *Document) setNodes(n *html.Node) {
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		switch c.Data {
		case "html":
			doc.html = c
			doc.setNodes(c)
		case "body":
			doc.body = c
		case "head":
			doc.head = c
		}
	}
}

// parseHead parses the ... part of the HTML page. Right now it
// only detects the <title>...</title>.
func (doc *Document) parseHead(n *html.Node) {
	if n.Type == html.ElementNode && n.Data == "title" {
		if chunk, err := NewChunk(doc, n); err == nil {
			doc.Title = chunk.Text
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		doc.parseHead(c)
	}
}

// countText counts the link text and the normal text per html.Node.
// "Link text" is text inside  tags and "normal text" is text inside
// anything but  tags. Of course, counting is done cumulative, so the
// numbers of a parent node include the numbers of it's child nodes.
func (doc *Document) countText(n *html.Node, insideLink bool) (linkText int, normText int) {
	linkText = 0
	normText = 0
	if n.Type == html.ElementNode && n.Data == "a" {
		insideLink = true
	}
	for s := n.FirstChild; s != nil; s = s.NextSibling {
		linkTextChild, normTextChild := doc.countText(s, insideLink)
		linkText += linkTextChild
		normText += normTextChild
	}
	if n.Type == html.TextNode {
		count := 0
		for _, rune := range n.Data {
			if unicode.IsLetter(rune) {
				count += 1
			}
		}
		if insideLink {
			linkText += count
		} else {
			normText += count
		}
	}
	doc.linkText[n] = linkText
	doc.normText[n] = normText
	return
}

// cleanBody removes unwanted HTML elements from the HTML body.
func (doc *Document) cleanBody(n *html.Node, level int) {

	// removeNode returns true if a node should be removed from HTML document.
	removeNode := func(c *html.Node, level int) bool {
		switch c.Data {
		// Elements save to ignore.
		case "address", "audio", "button", "canvas", "caption", "fieldset",
			"figcaption", "figure", "footer", "form", "frame", "iframe",
			"map", "menu", "nav", "noscript", "object", "option", "output",
			"script", "select", "style", "svg", "textarea", "video":
			return true
		// High-level tables might be used to layout the document, so we better
		// not ignore them.
		case "table":
			return level > 5
		}
		return false
	}

	var curr *html.Node = n.FirstChild
	var next *html.Node = nil
	for ; curr != nil; curr = next {
		// We have to remember the next sibling here becase calling RemoveChild
		// sets curr's NextSibling pointer to nil and we would quit the loop
		// prematurely.
		next = curr.NextSibling
		if curr.Type == html.ElementNode {
			if removeNode(curr, level) {
				n.RemoveChild(curr)
			} else {
				doc.cleanBody(curr, level+1)
			}
		}
	}
}

var (
	ignoreNames = util.NewRegexFromWords(
		"breadcrumb",
		"byline",
		"caption",
		"comment",
		"community",
		"credit",
		"description",
		"email",
		"foot",
		"gallery",
		"hide",
		"photo",
		"related",
		"shares",
		"social",
		"story[-_]?bar",
		"story[-_]?feature",
	)
	ignoreStyle = util.NewRegex(`(?i)display:\s*none`)
)

// parseBody parses the ... part of the HTML page. It creates
// Chunks for every html.TextNode found in the body.
func (doc *Document) parseBody(n *html.Node) {
	switch n.Type {
	case html.ElementNode:
		// We ignore the node if it has some nasty classes/ids/itemprobs or if
		// it contains "display: none" in its style attribute.
		for _, attr := range n.Attr {
			switch attr.Key {
			case "id", "class", "itemprop":
				if ignoreNames.In(attr.Val) {
					return
				}
			case "style":
				if ignoreStyle.In(attr.Val) {
					return
				}
			}
		}
		ancestorMask := 0
		switch n.Data {
		// We convert headings and links to text immediately. This is easier
		// and feasible because headings and links don't contain many children.
		// Descending into these children and handling every TextNode separately
		// would make things unnecessary complicated and our results noisy.
		case "h1", "h2", "h3", "h4", "h5", "h6", "a":
			if chunk, err := NewChunk(doc, n); err == nil {
				doc.Chunks = append(doc.Chunks, chunk)
			}
			return
		// Now mask the element type, but only if it isn't already set.
		// If we mask a bit which was already set by one of our callers, we'd also
		// clear it at the end of this function, though it actually should be cleared
		// by the caller.
		case "article":
			ancestorMask = AncestorArticle &^ doc.ancestors
		case "aside":
			ancestorMask = AncestorAside &^ doc.ancestors
		case "blockquote":
			ancestorMask = AncestorBlockquote &^ doc.ancestors
		case "ul", "ol":
			ancestorMask = AncestorList &^ doc.ancestors
		}
		// Add our mask to the ancestor bitmask.
		doc.ancestors |= ancestorMask
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			doc.parseBody(c)
		}
		// Remove our mask from the ancestor bitmask.
		doc.ancestors &^= ancestorMask
	case html.TextNode:
		if chunk, err := NewChunk(doc, n); err == nil {
			doc.Chunks = append(doc.Chunks, chunk)
		}
	}
}

type TextStat struct {
	Words     int
	Sentences int
	Count     int
}

// GetClassStats groups the document chunks by their classes (defined by the
// class attribute of HTML nodes) and calculates TextStats for each class.
func (doc *Document) GetClassStats() map[string]*TextStat {
	result := make(map[string]*TextStat)
	for _, chunk := range doc.Chunks {
		for _, class := range chunk.Classes {
			if stat, ok := result[class]; ok {
				stat.Words += chunk.Text.Words
				stat.Sentences += chunk.Text.Sentences
				stat.Count += 1
			} else {
				result[class] = &TextStat{chunk.Text.Words, chunk.Text.Sentences, 1}
			}
		}
	}
	return result
}

// GetClusterStats groups the document chunks by common ancestors and
// calculates TextStats for each group of chunks.
func (doc *Document) GetClusterStats() map[*Chunk]*TextStat {
	// Don't ascend further than this constant.
	const maxAncestors = 3

	// Count TextStats for Chunk ancestors.
	ancestorStat := make(map[*html.Node]*TextStat)
	for _, chunk := range doc.Chunks {
		node, count := chunk.Block, 0
		for node != nil && count < maxAncestors {
			if stat, ok := ancestorStat[node]; ok {
				stat.Words += chunk.Text.Words
				stat.Sentences += chunk.Text.Sentences
				stat.Count += 1
			} else {
				ancestorStat[node] = &TextStat{chunk.Text.Words, chunk.Text.Sentences, 1}
			}
			node, count = node.Parent, count+1
		}
	}

	// Generate result.
	result := make(map[*Chunk]*TextStat)
	for _, chunk := range doc.Chunks {
		node := chunk.Block
		if node == nil {
			continue
		}
		// Start with the parent's TextStat. Then ascend and check if the
		// current chunk has an ancestor with better stats. Use the best stat
		// as result.
		stat := ancestorStat[node]
		for {
			if node = node.Parent; node == nil {
				break
			}
			if statPrev, ok := ancestorStat[node]; ok {
				if stat.Count < statPrev.Count {
					stat = statPrev
				}
			} else {
				break
			}
		}
		result[chunk] = stat
	}
	return result
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"time"

	"github.com/gogf/gf/text/gstr"
)

// Model is the DAO for ORM.
type Model struct {
	db            DB             // Underlying DB interface.
	tx            *TX            // Underlying TX interface.
	schema        string         // Custom database schema.
	linkType      int            // Mark for operation on master or slave.
	tablesInit    string         // Table names when model initialization.
	tables        string         // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
	fields        string         // Operation fields, multiple fields joined using char ','.
	fieldsEx      string         // Excluded operation fields, multiple fields joined using char ','.
	extraArgs     []interface{}  // Extra custom arguments for sql.
	whereHolder   []*whereHolder // Condition strings for where operation.
	groupBy       string         // Used for "group by" statement.
	orderBy       string         // Used for "order by" statement.
	start         int            // Used for "select ... start, limit ..." statement.
	limit         int            // Used for "select ... start, limit ..." statement.
	option        int            // Option for extra operation features.
	offset        int            // Offset statement for some databases grammar.
	data          interface{}    // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
	batch         int            // Batch number for batch Insert/Replace/Save operations.
	filter        bool           // Filter data and where key-value pairs according to the fields of the table.
	lockInfo      string         // Lock for update or in shared lock.
	cacheEnabled  bool           // Enable sql result cache feature.
	cacheDuration time.Duration  // Cache TTL duration.
	cacheName     string         // Cache name for custom operation.
	safe          bool           // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
}

// whereHolder is the holder for where condition preparing.
type whereHolder struct {
	operator int           // Operator for this holder.
	where    interface{}   // Where parameter.
	args     []interface{} // Arguments for where parameter.
}

const (
	gLINK_TYPE_MASTER   = 1
	gLINK_TYPE_SLAVE    = 2
	gWHERE_HOLDER_WHERE = 1
	gWHERE_HOLDER_AND   = 2
	gWHERE_HOLDER_OR    = 3
	OPTION_OMITEMPTY    = 1 << iota
	OPTION_ALLOWEMPTY
)

// Table creates and returns a new ORM model from given schema.
// The parameter <tables> can be more than one table names, like :
// "user", "user u", "user, user_detail", "user u, user_detail ud"
func (c *Core) Table(table string) *Model {
	table = c.DB.QuotePrefixTableName(table)
	return &Model{
		db:         c.DB,
		tablesInit: table,
		tables:     table,
		fields:     "*",
		start:      -1,
		offset:     -1,
		safe:       false,
		option:     OPTION_ALLOWEMPTY,
	}
}

// Model is alias of Core.Table.
// See Core.Table.
func (c *Core) Model(table string) *Model {
	return c.DB.Table(table)
}

// From is alias of Core.Table.
// See Core.Table.
// Deprecated.
func (c *Core) From(table string) *Model {
	return c.DB.Table(table)
}

// Table acts like Core.Table except it operates on transaction.
// See Core.Table.
func (tx *TX) Table(table string) *Model {
	table = tx.db.QuotePrefixTableName(table)
	return &Model{
		db:         tx.db,
		tx:         tx,
		tablesInit: table,
		tables:     table,
		fields:     "*",
		start:      -1,
		offset:     -1,
		safe:       false,
		option:     OPTION_ALLOWEMPTY,
	}
}

// Model is alias of tx.Table.
// See tx.Table.
func (tx *TX) Model(table string) *Model {
	return tx.Table(table)
}

// From is alias of tx.Table.
// See tx.Table.
// Deprecated.
func (tx *TX) From(table string) *Model {
	return tx.Table(table)
}

// As sets an alias name for current table.
func (m *Model) As(as string) *Model {
	if m.tables != "" {
		model := m.getModel()
		model.tables = gstr.TrimRight(model.tables) + " AS " + as
		return model
	}
	return m
}

// DB sets/changes the db object for current operation.
func (m *Model) DB(db DB) *Model {
	model := m.getModel()
	model.db = db
	return model
}

// TX sets/changes the transaction for current operation.
func (m *Model) TX(tx *TX) *Model {
	model := m.getModel()
	model.tx = tx
	return model
}

// Schema sets the schema for current operation.
func (m *Model) Schema(schema string) *Model {
	model := m.getModel()
	model.schema = schema
	return model
}

// Clone creates and returns a new model which is a clone of current model.
// Note that it uses deep-copy for the clone.
func (m *Model) Clone() *Model {
	newModel := (*Model)(nil)
	if m.tx != nil {
		newModel = m.tx.Table(m.tablesInit)
	} else {
		newModel = m.db.Table(m.tablesInit)
	}
	*newModel = *m
	// Deep copy slice attributes.
	if n := len(m.extraArgs); n > 0 {
		newModel.extraArgs = make([]interface{}, n)
		copy(newModel.extraArgs, m.extraArgs)
	}
	if n := len(m.whereHolder); n > 0 {
		newModel.whereHolder = make([]*whereHolder, n)
		copy(newModel.whereHolder, m.whereHolder)
	}
	return newModel
}

// Master marks the following operation on master node.
func (m *Model) Master() *Model {
	model := m.getModel()
	model.linkType = gLINK_TYPE_MASTER
	return model
}

// Slave marks the following operation on slave node.
// Note that it makes sense only if there's any slave node configured.
func (m *Model) Slave() *Model {
	model := m.getModel()
	model.linkType = gLINK_TYPE_SLAVE
	return model
}

// Safe marks this model safe or unsafe. If safe is true, it clones and returns a new model object
// whenever the operation done, or else it changes the attribute of current model.
func (m *Model) Safe(safe ...bool) *Model {
	if len(safe) > 0 {
		m.safe = safe[0]
	} else {
		m.safe = true
	}
	return m
}
<commit_msg>change default value of safe from false to true for gdb.Model<commit_after>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"time"

	"github.com/gogf/gf/text/gstr"
)

// Model is the DAO for ORM.
type Model struct {
	db            DB             // Underlying DB interface.
	tx            *TX            // Underlying TX interface.
	schema        string         // Custom database schema.
	linkType      int            // Mark for operation on master or slave.
	tablesInit    string         // Table names when model initialization.
	tables        string         // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
	fields        string         // Operation fields, multiple fields joined using char ','.
	fieldsEx      string         // Excluded operation fields, multiple fields joined using char ','.
	extraArgs     []interface{}  // Extra custom arguments for sql.
	whereHolder   []*whereHolder // Condition strings for where operation.
	groupBy       string         // Used for "group by" statement.
	orderBy       string         // Used for "order by" statement.
	start         int            // Used for "select ... start, limit ..." statement.
	limit         int            // Used for "select ... start, limit ..." statement.
	option        int            // Option for extra operation features.
	offset        int            // Offset statement for some databases grammar.
	data          interface{}    // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
	batch         int            // Batch number for batch Insert/Replace/Save operations.
	filter        bool           // Filter data and where key-value pairs according to the fields of the table.
	lockInfo      string         // Lock for update or in shared lock.
	cacheEnabled  bool           // Enable sql result cache feature.
	cacheDuration time.Duration  // Cache TTL duration.
	cacheName     string         // Cache name for custom operation.
	safe          bool           // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
}

// whereHolder is the holder for where condition preparing.
type whereHolder struct {
	operator int           // Operator for this holder.
	where    interface{}   // Where parameter.
	args     []interface{} // Arguments for where parameter.
}

const (
	gLINK_TYPE_MASTER   = 1
	gLINK_TYPE_SLAVE    = 2
	gWHERE_HOLDER_WHERE = 1
	gWHERE_HOLDER_AND   = 2
	gWHERE_HOLDER_OR    = 3
	OPTION_OMITEMPTY    = 1 << iota
	OPTION_ALLOWEMPTY
)

// Table creates and returns a new ORM model from given schema.
// The parameter <tables> can be more than one table names, like :
// "user", "user u", "user, user_detail", "user u, user_detail ud"
func (c *Core) Table(table string) *Model {
	table = c.DB.QuotePrefixTableName(table)
	return &Model{
		db:         c.DB,
		tablesInit: table,
		tables:     table,
		fields:     "*",
		start:      -1,
		offset:     -1,
		safe:       true,
		option:     OPTION_ALLOWEMPTY,
	}
}

// Model is alias of Core.Table.
// See Core.Table.
func (c *Core) Model(table string) *Model {
	return c.DB.Table(table)
}

// From is alias of Core.Table.
// See Core.Table.
// Deprecated.
func (c *Core) From(table string) *Model {
	return c.DB.Table(table)
}

// Table acts like Core.Table except it operates on transaction.
// See Core.Table.
func (tx *TX) Table(table string) *Model {
	table = tx.db.QuotePrefixTableName(table)
	return &Model{
		db:         tx.db,
		tx:         tx,
		tablesInit: table,
		tables:     table,
		fields:     "*",
		start:      -1,
		offset:     -1,
		safe:       true,
		option:     OPTION_ALLOWEMPTY,
	}
}

// Model is alias of tx.Table.
// See tx.Table.
func (tx *TX) Model(table string) *Model {
	return tx.Table(table)
}

// From is alias of tx.Table.
// See tx.Table.
// Deprecated.
func (tx *TX) From(table string) *Model {
	return tx.Table(table)
}

// As sets an alias name for current table.
func (m *Model) As(as string) *Model {
	if m.tables != "" {
		model := m.getModel()
		model.tables = gstr.TrimRight(model.tables) + " AS " + as
		return model
	}
	return m
}

// DB sets/changes the db object for current operation.
func (m *Model) DB(db DB) *Model {
	model := m.getModel()
	model.db = db
	return model
}

// TX sets/changes the transaction for current operation.
func (m *Model) TX(tx *TX) *Model {
	model := m.getModel()
	model.tx = tx
	return model
}

// Schema sets the schema for current operation.
func (m *Model) Schema(schema string) *Model {
	model := m.getModel()
	model.schema = schema
	return model
}

// Clone creates and returns a new model which is a clone of current model.
// Note that it uses deep-copy for the clone.
func (m *Model) Clone() *Model {
	newModel := (*Model)(nil)
	if m.tx != nil {
		newModel = m.tx.Table(m.tablesInit)
	} else {
		newModel = m.db.Table(m.tablesInit)
	}
	*newModel = *m
	// Deep copy slice attributes.
	if n := len(m.extraArgs); n > 0 {
		newModel.extraArgs = make([]interface{}, n)
		copy(newModel.extraArgs, m.extraArgs)
	}
	if n := len(m.whereHolder); n > 0 {
		newModel.whereHolder = make([]*whereHolder, n)
		copy(newModel.whereHolder, m.whereHolder)
	}
	return newModel
}

// Master marks the following operation on master node.
func (m *Model) Master() *Model {
	model := m.getModel()
	model.linkType = gLINK_TYPE_MASTER
	return model
}

// Slave marks the following operation on slave node.
// Note that it makes sense only if there's any slave node configured.
func (m *Model) Slave() *Model {
	model := m.getModel()
	model.linkType = gLINK_TYPE_SLAVE
	return model
}

// Safe marks this model safe or unsafe. If safe is true, it clones and returns a new model object
// whenever the operation done, or else it changes the attribute of current model.
func (m *Model) Safe(safe ...bool) *Model {
	if len(safe) > 0 {
		m.safe = safe[0]
	} else {
		m.safe = true
	}
	return m
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"testing"

	"command"
)

type testApp struct {
	cmdRoot           *command.CmdNode
	confRootCandidate *command.ConfNode
	confRootActive    *command.ConfNode
}

func (a testApp) CmdRoot() *command.CmdNode {
	return a.cmdRoot
}
func (a testApp) ConfRootCandidate() *command.ConfNode {
	return a.confRootCandidate
}
func (a testApp) ConfRootActive() *command.ConfNode {
	return a.confRootActive
}

type testClient struct {
	outputChannel chan string
}

func (c testClient) ConfigPath() string {
	return ""
}
func (c testClient) Status() int {
	return command.CONF
}
func (c testClient) ConfigPathSet(path string) {}
func (c testClient) Send(msg string)           {}
func (c testClient) Sendln(msg string)         {}
func (c testClient) SendlnNow(msg string)      {}
func (c testClient) InputQuit()                {}
func (c testClient) Output() chan<- string {
	return c.outputChannel
}

func TestConf(t *testing.T) {

	app := &testApp{
		cmdRoot:           &command.CmdNode{MinLevel: command.EXEC},
		confRootCandidate: &command.ConfNode{},
		confRootActive:    &command.ConfNode{},
	}

	root := app.cmdRoot
	cmdNone := command.CMD_NONE
	cmdConf := command.CMD_CONF

	cmdBogus := func(ctx command.ConfContext, node *command.CmdNode, line string, c command.CmdClient) {
	}

	command.CmdInstall(root, cmdConf, "interface {IFNAME} description {ANY}", command.CONF, command.HelperDescription, "Set interface description")
	command.CmdInstall(root, cmdConf, "interface {IFNAME} ipv4 address {IPADDR}", command.CONF, cmdBogus, "Assign IPv4 address to interface")
	command.CmdInstall(root, cmdConf, "interface {IFNAME} ipv6 address {IPADDR6}", command.CONF, cmdBogus, "Assign IPv6 address to interface")
	command.CmdInstall(root, cmdConf, "interface {IFNAME} shutdown", command.CONF, cmdBogus, "Disable interface")
	command.CmdInstall(root, cmdConf, "ip routing", command.CONF, cmdBogus, "Enable IP routing")
	command.CmdInstall(root, cmdConf, "hostname {HOSTNAME}", command.CONF, command.HelperHostname, "Assign hostname")
	command.CmdInstall(root, cmdNone, "no {ANY}", command.CONF, command.HelperNo, "Remove a configuration item")

	c := &testClient{outputChannel: make(chan string)}

	go func() {
		for {
			line, ok := <-c.outputChannel
			if !ok {
				break
			}
			fmt.Printf("TestConf: read bogus client output channel: [%s]", line)
		}
		fmt.Printf("TestConf: exiting")
	}()

	dispatchCommand(app, "hostname nexthop-router", c, command.CONF)
	if host := hostname(app); host != "nexthop-router" {
		t.Errorf("bad hostname: %s", host)
	}

	dispatchCommand(app, "int eth0 desc  aa  bb   ccc", c, command.CONF)
	node, err := app.confRootCandidate.Get("interface eth0 description")
	if err != nil {
		t.Errorf("bad description: %v", err)
	}
	if node.Path != "interface eth0 description" {
		t.Errorf("bad description path: [%s]", node.Path)
	}
	if len(node.Value) != 1 {
		t.Errorf("bad description value count: %d", len(node.Value))
	}
	if node.Value[0] != " aa  bb   ccc" {
		t.Errorf("bad description value: [%s]", node.Value[0])
	}

	dispatchCommand(app, "no int eth0 desc xxxxxxx", c, command.CONF)
	node, err = app.confRootCandidate.Get("interface eth0 description")
	if node != nil || err == nil {
		t.Errorf("description should not be present: node=[%v] error=[%v]", node, err)
	}

	dispatchCommand(app, "int eth1 desc  aa  bb   ccc", c, command.CONF)

	dispatchCommand(app, "no int eth1 desc", c, command.CONF)

	close(c.outputChannel)
}
<commit_msg>More interface description testing.<commit_after>package cli

import (
	"fmt"
	"testing"

	"command"
)

type testApp struct {
	cmdRoot           *command.CmdNode
	confRootCandidate *command.ConfNode
	confRootActive    *command.ConfNode
}

func (a testApp) CmdRoot() *command.CmdNode {
	return a.cmdRoot
}
func (a testApp) ConfRootCandidate() *command.ConfNode {
	return a.confRootCandidate
}
func (a testApp) ConfRootActive() *command.ConfNode {
	return a.confRootActive
}

type testClient struct {
	outputChannel chan string
}

func (c testClient) ConfigPath() string {
	return ""
}
func (c testClient) Status() int {
	return command.CONF
}
func (c testClient) ConfigPathSet(path string) {}
func (c testClient) Send(msg string)           {}
func (c testClient) Sendln(msg string)         {}
func (c testClient) SendlnNow(msg string)      {}
func (c testClient) InputQuit()                {}
func (c testClient) Output() chan<- string {
	return c.outputChannel
}

func TestConf(t *testing.T) {

	app := &testApp{
		cmdRoot:           &command.CmdNode{MinLevel: command.EXEC},
		confRootCandidate: &command.ConfNode{},
		confRootActive:    &command.ConfNode{},
	}

	root := app.cmdRoot
	cmdNone := command.CMD_NONE
	cmdConf := command.CMD_CONF

	cmdBogus := func(ctx command.ConfContext, node *command.CmdNode, line string, c command.CmdClient) {
	}

	command.CmdInstall(root, cmdConf, "interface {IFNAME} description {ANY}", command.CONF, command.HelperDescription, "Set interface description")
	command.CmdInstall(root, cmdConf, "interface {IFNAME} ipv4 address {IPADDR}", command.CONF, cmdBogus, "Assign IPv4 address to interface")
	command.CmdInstall(root, cmdConf, "interface {IFNAME} ipv6 address {IPADDR6}", command.CONF, cmdBogus, "Assign IPv6 address to interface")
	command.CmdInstall(root, cmdConf, "interface {IFNAME} shutdown", command.CONF, cmdBogus, "Disable interface")
	command.CmdInstall(root, cmdConf, "ip routing", command.CONF, cmdBogus, "Enable IP routing")
	command.CmdInstall(root, cmdConf, "hostname {HOSTNAME}", command.CONF, command.HelperHostname, "Assign hostname")
	command.CmdInstall(root, cmdNone, "no {ANY}", command.CONF, command.HelperNo, "Remove a configuration item")

	c := &testClient{outputChannel: make(chan string)}

	go func() {
		for {
			line, ok := <-c.outputChannel
			if !ok {
				break
			}
			fmt.Printf("TestConf: read bogus client output channel: [%s]", line)
		}
		fmt.Printf("TestConf: exiting")
	}()

	dispatchCommand(app, "hostname nexthop-router", c, command.CONF)
	if host := hostname(app); host != "nexthop-router" {
		t.Errorf("bad hostname: %s", host)
	}

	dispatchCommand(app, "int eth0 desc  aa  bb   ccc", c, command.CONF)
	node, err := app.confRootCandidate.Get("interface eth0 description")
	if err != nil {
		t.Errorf("bad description: %v", err)
	}
	if node.Path != "interface eth0 description" {
		t.Errorf("bad description path: [%s]", node.Path)
	}
	if len(node.Value) != 1 {
		t.Errorf("bad description value count: %d", len(node.Value))
	}
	if node.Value[0] != " aa  bb   ccc" {
		t.Errorf("bad description value: [%s]", node.Value[0])
	}

	dispatchCommand(app, "no int eth0 desc xxxxxxx", c, command.CONF)
	node, err = app.confRootCandidate.Get("interface eth0 description")
	if node != nil || err == nil {
		t.Errorf("eth0 description should not be present: node=[%v] error=[%v]", node, err)
	}

	dispatchCommand(app, "int eth1 desc ddd   eee   fff ", c, command.CONF)
	node, err = app.confRootCandidate.Get("interface eth1 description")
	if err != nil {
		t.Errorf("bad description: %v", err)
	}
	if node.Path != "interface eth1 description" {
		t.Errorf("bad description path: [%s]", node.Path)
	}
	if len(node.Value) != 1 {
		t.Errorf("bad description value count: %d", len(node.Value))
	}
	if node.Value[0] != "ddd   eee   fff " {
		t.Errorf("bad description value: [%s]", node.Value[0])
	}

	dispatchCommand(app, "no int eth1 desc", c, command.CONF)
	node, err = app.confRootCandidate.Get("interface eth1 description")
	if node != nil || err == nil {
		t.Errorf("eth1 description should not be present: node=[%v] error=[%v]", node, err)
	}

	close(c.outputChannel)
}
<|endoftext|>


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

// The canon command adds canonical import paths to Go packages.
package main // import "willnorris.com/go/tools/canon"

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/format"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func usage() {
	fmt.Fprint(os.Stderr, "usage: canon [packages]\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if len(flag.Args()) == 0 {
		flag.Usage()
		return
	}

	if err := fixPackages(flag.Args()...); err != nil {
		log.Fatalf("error listing packages: %v", err)
	}
}

func fixPackages(packages ...string) error {
	pkgs, err := list(packages...)
	if err != nil {
		return err
	}

	for _, pkg := range pkgs {
		if strings.Contains(pkg.ImportPath, "/vendor/") || strings.Contains(pkg.ImportPath, "/third_party/") {
			// skip vendored packages
			continue
		}
		if len(pkg.GoFiles) == 0 {
			// skip packages with no go files
			continue
		}
		if pkg.ImportComment != "" {
			if pkg.ImportComment != pkg.ImportPath {
				return fmt.Errorf("package %q does not having matching import comment %q", pkg.ImportPath, pkg.ImportComment)
			}
			continue
		}
		if err := fixPackage(pkg); err != nil {
			return err
		}
	}

	return nil
}

func fixPackage(pkg *build.Package) error {
	for _, file := range pkg.GoFiles {
		filename := filepath.Join(pkg.Dir, file)

		fset := token.NewFileSet()
		pf, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
		if err != nil {
			return err
		}

		if pf.Doc != nil {
			return rewriteFile(fset, pf, filename, pkg.ImportPath)
		}
	}

	// no files have package docs.  look for file that matches pkg.Name
	for _, file := range pkg.GoFiles {
		if file == pkg.Name+".go" {
			return parseAndRewriteFile(file, pkg)
		}
	}

	log.Printf("can't find file to rewrite for package: %q (%v)", pkg.ImportPath, pkg.Name)
	return nil
}

func parseAndRewriteFile(file string, pkg *build.Package) error {
	filename := filepath.Join(pkg.Dir, file)
	fset := token.NewFileSet()
	pf, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
	if err != nil {
		return err
	}

	return rewriteFile(fset, pf, filename, pkg.ImportPath)
}

// rewrite filename to include importPath.
func rewriteFile(fset *token.FileSet, pf *ast.File, filename, importPath string) error {
	log.Printf("package: %q, rewriting %q", importPath, filename)
	// add comment containing canonical import path
	cmap := ast.NewCommentMap(fset, pf, pf.Comments)
	com := &ast.Comment{Slash: pf.Name.End(), Text: `// import "` + importPath + `"`}
	cmap[pf.Name] = []*ast.CommentGroup{{List: []*ast.Comment{com}}}
	pf.Comments = cmap.Comments()

	var buf bytes.Buffer
	if err := format.Node(&buf, fset, pf); err != nil {
		return err
	}
	return ioutil.WriteFile(filename, buf.Bytes(), 0644)
}

// list runs 'go list' with the specified arguments and returns the metadata
// for matching packages.
func list(args ...string) ([]*build.Package, error) {
	cmd := exec.Command("go", append([]string{"list", "-e", "-json"}, args...)...)
	cmd.Stdout = new(bytes.Buffer)
	if err := cmd.Run(); err != nil {
		return nil, err
	}

	dec := json.NewDecoder(cmd.Stdout.(io.Reader))
	var pkgs []*build.Package
	for {
		var p build.Package
		if err := dec.Decode(&p); err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		pkgs = append(pkgs, &p)
	}
	return pkgs, nil
}
<commit_msg>don't skip vendor or third_party directories<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The canon command adds canonical import paths to Go packages.
package main // import "willnorris.com/go/tools/canon"

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/format"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

func usage() {
	fmt.Fprint(os.Stderr, "usage: canon [packages]\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if len(flag.Args()) == 0 {
		flag.Usage()
		return
	}

	if err := fixPackages(flag.Args()...); err != nil {
		log.Fatalf("error listing packages: %v", err)
	}
}

func fixPackages(packages ...string) error {
	pkgs, err := list(packages...)
	if err != nil {
		return err
	}

	for _, pkg := range pkgs {
		if len(pkg.GoFiles) == 0 {
			// skip packages with no go files
			continue
		}
		if pkg.ImportComment != "" {
			if pkg.ImportComment != pkg.ImportPath {
				return fmt.Errorf("package %q does not having matching import comment %q", pkg.ImportPath, pkg.ImportComment)
			}
			continue
		}
		if err := fixPackage(pkg); err != nil {
			return err
		}
	}

	return nil
}

func fixPackage(pkg *build.Package) error {
	for _, file := range pkg.GoFiles {
		filename := filepath.Join(pkg.Dir, file)

		fset := token.NewFileSet()
		pf, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
		if err != nil {
			return err
		}

		if pf.Doc != nil {
			return rewriteFile(fset, pf, filename, pkg.ImportPath)
		}
	}

	// no files have package docs.  look for file that matches pkg.Name
	for _, file := range pkg.GoFiles {
		if file == pkg.Name+".go" {
			return parseAndRewriteFile(file, pkg)
		}
	}

	log.Printf("can't find file to rewrite for package: %q (%v)", pkg.ImportPath, pkg.Name)
	return nil
}

func parseAndRewriteFile(file string, pkg *build.Package) error {
	filename := filepath.Join(pkg.Dir, file)
	fset := token.NewFileSet()
	pf, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
	if err != nil {
		return err
	}

	return rewriteFile(fset, pf, filename, pkg.ImportPath)
}

// rewrite filename to include importPath.
func rewriteFile(fset *token.FileSet, pf *ast.File, filename, importPath string) error {
	log.Printf("package: %q, rewriting %q", importPath, filename)
	// add comment containing canonical import path
	cmap := ast.NewCommentMap(fset, pf, pf.Comments)
	com := &ast.Comment{Slash: pf.Name.End(), Text: `// import "` + importPath + `"`}
	cmap[pf.Name] = []*ast.CommentGroup{{List: []*ast.Comment{com}}}
	pf.Comments = cmap.Comments()

	var buf bytes.Buffer
	if err := format.Node(&buf, fset, pf); err != nil {
		return err
	}
	return ioutil.WriteFile(filename, buf.Bytes(), 0644)
}

// list runs 'go list' with the specified arguments and returns the metadata
// for matching packages.
func list(args ...string) ([]*build.Package, error) {
	cmd := exec.Command("go", append([]string{"list", "-e", "-json"}, args...)...)
	cmd.Stdout = new(bytes.Buffer)
	if err := cmd.Run(); err != nil {
		return nil, err
	}

	dec := json.NewDecoder(cmd.Stdout.(io.Reader))
	var pkgs []*build.Package
	for {
		var p build.Package
		if err := dec.Decode(&p); err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		pkgs = append(pkgs, &p)
	}
	return pkgs, nil
}
<|endoftext|>


<commit_before>package drivers

import "testing"

func TestIsFileRoot(t *testing.T) {
	is, err := isFile("/") //Is Folder so shouldn't be ok
	if is {
		t.Error("Expected false, got ", is, err)
	}
}
func TestIsFileNotExist(t *testing.T) {
	is, err := isFile("/dshdskjdkjs/sdjsjdsjks") //ls should be nok on all host
	if is {
		t.Error("Expected false, got ", is, err)
	}
}
func TestIsFileLS(t *testing.T) {
	is, err := isFile("/usr/bin/ls") //ls should be ok on all host
	if !is || err != nil {
		t.Error("Expected true without error, got ", is, err)
	}
}

func TestUrlToDriverFTP(t *testing.T) {
	d, err := urlToDriver("ftp://host") //Is Folder so shouldn't be ok
	if err != nil || d.id() != FTP {
		t.Error("Expected a FTP driver, got ", d, err)
	}
}
func TestUrlToDriverSSH(t *testing.T) {
	d, err := urlToDriver("ssh://host")
	if err != nil || d.id() != SSH {
		t.Error("Expected a SSH driver, got ", d, err)
	}
}
func TestUrlToDriverSFTP(t *testing.T) {
	d, err := urlToDriver("sftp://host")
	if err != nil || d.id() != SSH {
		t.Error("Expected a SSH driver, got ", d, err)
	}
}
func TestUrlToDriverSMB(t *testing.T) {
	d, err := urlToDriver("smb://host")
	if err != nil || d.id() != SMB {
		t.Error("Expected a SMB driver, got ", d, err)
	}
}
func TestUrlToDriverDav(t *testing.T) {
	d, err := urlToDriver("dav://host")
	if err != nil || d.id() != DAV {
		t.Error("Expected a DAV driver, got ", d, err)
	}
}
func TestUrlToDriverDavs(t *testing.T) {
	d, err := urlToDriver("davs://host")
	if err != nil || d.id() != DAV {
		t.Error("Expected a DAV driver, got ", d, err)
	}
}
func TestUrlToDriverNFS(t *testing.T) {
	d, err := urlToDriver("nfs://host")
	if err != nil || d.id() != NFS {
		t.Error("Expected a NFS driver, got ", d, err)
	}
}
func TestUrlToDriverUnkownError(t *testing.T) {
	d, err := urlToDriver("ltp://host")
	if err == nil {
		t.Error("Expected a error, got ", d, err)
	}
}
func TestUrlToDriverURLError(t *testing.T) {
	d, err := urlToDriver("host")
	if err == nil {
		t.Error("Expected a error, got ", d, err)
	}
}
<commit_msg>Update tools_test.go<commit_after>package drivers

import "testing"

func TestIsFileRoot(t *testing.T) {
	is, err := isFile("/") //Is Folder so shouldn't be ok
	if is {
		t.Error("Expected false, got ", is, err)
	}
}
func TestIsFileNotExist(t *testing.T) {
	is, err := isFile("/dshdskjdkjs/sdjsjdsjks") //ls should be nok on all host
	if is {
		t.Error("Expected false, got ", is, err)
	}
}
func TestIsFileLS(t *testing.T) {
	is, err := isFile("/bin/ls") //ls should be ok on all host
	if !is || err != nil {
		t.Error("Expected true without error, got ", is, err)
	}
}

func TestUrlToDriverFTP(t *testing.T) {
	d, err := urlToDriver("ftp://host") //Is Folder so shouldn't be ok
	if err != nil || d.id() != FTP {
		t.Error("Expected a FTP driver, got ", d, err)
	}
}
func TestUrlToDriverSSH(t *testing.T) {
	d, err := urlToDriver("ssh://host")
	if err != nil || d.id() != SSH {
		t.Error("Expected a SSH driver, got ", d, err)
	}
}
func TestUrlToDriverSFTP(t *testing.T) {
	d, err := urlToDriver("sftp://host")
	if err != nil || d.id() != SSH {
		t.Error("Expected a SSH driver, got ", d, err)
	}
}
func TestUrlToDriverSMB(t *testing.T) {
	d, err := urlToDriver("smb://host")
	if err != nil || d.id() != SMB {
		t.Error("Expected a SMB driver, got ", d, err)
	}
}
func TestUrlToDriverDav(t *testing.T) {
	d, err := urlToDriver("dav://host")
	if err != nil || d.id() != DAV {
		t.Error("Expected a DAV driver, got ", d, err)
	}
}
func TestUrlToDriverDavs(t *testing.T) {
	d, err := urlToDriver("davs://host")
	if err != nil || d.id() != DAV {
		t.Error("Expected a DAV driver, got ", d, err)
	}
}
func TestUrlToDriverNFS(t *testing.T) {
	d, err := urlToDriver("nfs://host")
	if err != nil || d.id() != NFS {
		t.Error("Expected a NFS driver, got ", d, err)
	}
}
func TestUrlToDriverUnkownError(t *testing.T) {
	d, err := urlToDriver("ltp://host")
	if err == nil {
		t.Error("Expected a error, got ", d, err)
	}
}
func TestUrlToDriverURLError(t *testing.T) {
	d, err := urlToDriver("host")
	if err == nil {
		t.Error("Expected a error, got ", d, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"runtime"
	"time"

	vbx "github.com/boot2docker/boot2docker-cli/virtualbox"
)

// Initialize the boot2docker VM from scratch.
func cmdInit() int {
	// TODO(@riobard) break up this command into multiple stages

	if ping(fmt.Sprintf("localhost:%d", B2D.DockerPort)) {
		logf("DOCKER_PORT=%d on localhost is occupied. Please choose another one.", B2D.DockerPort)
		return 1
	}

	if ping(fmt.Sprintf("localhost:%d", B2D.SSHPort)) {
		logf("SSH_PORT=%d on localhost is occupied. Please choose another one.", B2D.SSHPort)
		return 1
	}

	if _, err := os.Stat(B2D.ISO); err != nil {
		if !os.IsNotExist(err) {
			logf("Failed to open ISO image %q: %s", B2D.ISO, err)
			return 1
		}

		if exitcode := cmdDownload(); exitcode != 0 {
			return exitcode
		}
	}

	logf("Creating VM %s...", B2D.VM)
	m, err := vbx.CreateMachine(B2D.VM, "")
	if err != nil {
		logf("Failed to create VM %q: %s", B2D.VM, err)
		return 1
	}

	logf("Apply interim patch to VM %s (https://www.virtualbox.org/ticket/12748)", B2D.VM)
	if err := vbx.SetExtra(B2D.VM, "VBoxInternal/CPUM/EnableHVP", "1"); err != nil {
		logf("Failed to patch vm: %s", err)
		return 1
	}

	m.OSType = "Linux26_64"
	m.CPUs = uint(runtime.NumCPU())
	m.Memory = B2D.Memory

	m.Flag |= vbx.F_pae
	m.Flag |= vbx.F_longmode // important: use x86-64 processor
	m.Flag |= vbx.F_rtcuseutc
	m.Flag |= vbx.F_acpi
	m.Flag |= vbx.F_ioapic
	m.Flag |= vbx.F_hpet
	m.Flag |= vbx.F_hwvirtex
	m.Flag |= vbx.F_vtxvpid
	m.Flag |= vbx.F_largepages
	m.Flag |= vbx.F_nestedpaging

	m.BootOrder = []string{"dvd"}
	if err := m.Modify(); err != nil {
		logf("Failed to modify VM %q: %s", B2D.VM, err)
		return 1
	}

	logf("Setting NIC #1 to use NAT network...")
	if err := m.SetNIC(1, vbx.NIC{Network: vbx.NICNetNAT, Hardware: vbx.VirtIO}); err != nil {
		logf("Failed to add network interface to VM %q: %s", B2D.VM, err)
		return 1
	}

	pfRules := map[string]vbx.PFRule{
		"ssh":    vbx.PFRule{Proto: vbx.PFTCP, HostIP: net.ParseIP("127.0.0.1"), HostPort: B2D.SSHPort, GuestPort: 22},
		"docker": vbx.PFRule{Proto: vbx.PFTCP, HostIP: net.ParseIP("127.0.0.1"), HostPort: B2D.DockerPort, GuestPort: 4243},
	}

	for name, rule := range pfRules {
		if err := m.AddNATPF(1, name, rule); err != nil {
			logf("Failed to add port forwarding to VM %q: %s", B2D.VM, err)
			return 1
		}
		logf("Port forwarding [%s] %s", name, rule)
	}

	hostIFName, err := getHostOnlyNetworkInterface()
	if err != nil {
		logf("Failed to create host-only network interface: %s", err)
		return 1
	}

	logf("Setting NIC #2 to use host-only network %q...", hostIFName)
	if err := m.SetNIC(2, vbx.NIC{Network: vbx.NICNetHostonly, Hardware: vbx.VirtIO, HostonlyAdapter: hostIFName}); err != nil {
		logf("Failed to add network interface to VM %q: %s", B2D.VM, err)
		return 1
	}

	logf("Setting VM storage...")
	if err := m.AddStorageCtl("SATA", vbx.StorageController{SysBus: vbx.SysBusSATA, HostIOCache: true, Bootable: true}); err != nil {
		logf("Failed to add storage controller to VM %q: %s", B2D.VM, err)
		return 1
	}

	if err := m.AttachStorage("SATA", vbx.StorageMedium{Port: 0, Device: 0, DriveType: vbx.DriveDVD, Medium: B2D.ISO}); err != nil {
		logf("Failed to attach ISO image %q: %s", B2D.ISO, err)
		return 1
	}

	diskImg := filepath.Join(m.BaseFolder, fmt.Sprintf("%s.vmdk", B2D.VM))

	if _, err := os.Stat(diskImg); err != nil {
		if !os.IsNotExist(err) {
			logf("Failed to open disk image %q: %s", diskImg, err)
			return 1
		}

		if err := makeDiskImage(diskImg, B2D.DiskSize); err != nil {
			logf("Failed to create disk image %q: %s", diskImg, err)
			return 1
		}
	}

	if err := m.AttachStorage("SATA", vbx.StorageMedium{Port: 1, Device: 0, DriveType: vbx.DriveHDD, Medium: diskImg}); err != nil {
		logf("Failed to attach disk image %q: %s", diskImg, err)
		return 1
	}

	logf("Done. Type `%s up` to start the VM.", os.Args[0])
	return 0
}

// Bring up the VM from all possible states.
func cmdUp() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Start(); err != nil {
		logf("Failed to start machine %q: %s", B2D.VM, err)
		return 1
	}

	logf("Waiting for SSH server to start...")
	addr := fmt.Sprintf("localhost:%d", B2D.SSHPort)
	const n = 10
	// Try to connect to the SSH 10 times at 3 sec interval before giving up.
	if err := read(addr, n, 3*time.Second); err != nil {
		logf("Failed to connect to SSH port at %s after %d attempts. Last error: %v", addr, n, err)
		return 1
	}

	logf("Started.")

	switch runtime.GOOS {
	case "windows":
		logf("Docker client does not run on Windows for now. Please use")
		logf("    %s ssh", os.Args[0])
		logf("to SSH into the VM instead.")
	default:
		// Check if $DOCKER_HOST ENV var is properly configured.
		if os.Getenv("DOCKER_HOST") != fmt.Sprintf("tcp://localhost:%d", B2D.DockerPort) {
			logf("To connect the Docker client to the Docker daemon, please set:")
			logf("    export DOCKER_HOST=tcp://localhost:%d", B2D.DockerPort)
		}
	}
	return 0
}

// Suspend and save the current state of VM on disk.
func cmdSave() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Save(); err != nil {
		logf("Failed to save machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Gracefully stop the VM by sending ACPI shutdown signal.
func cmdStop() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Stop(); err != nil {
		logf("Failed to stop machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Forcefully power off the VM (equivalent to unplug power). Might corrupt disk
// image.
func cmdPoweroff() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Poweroff(); err != nil {
		logf("Failed to poweroff machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Gracefully stop and then start the VM.
func cmdRestart() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Restart(); err != nil {
		logf("Failed to restart machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Forcefully reset (equivalent to cold boot) the VM. Might corrupt disk image.
func cmdReset() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Reset(); err != nil {
		logf("Failed to reset machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Delete the VM and associated disk image.
func cmdDelete() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		if err == vbx.ErrMachineNotExist {
			logf("Machine %q does not exist.", B2D.VM)
			return 0
		}
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Delete(); err != nil {
		logf("Failed to delete machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Show detailed info of the VM.
func cmdInfo() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := json.NewEncoder(os.Stdout).Encode(m); err != nil {
		logf("Failed to encode machine %q info: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Show the current state of the VM.
func cmdStatus() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	fmt.Println(m.State)
	return 0
}

// Call the external SSH command to login into boot2docker VM.
func cmdSSH() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}

	if m.State != vbx.Running {
		logf("VM %q is not running.", B2D.VM)
		return 1
	}

	// TODO What SSH client is used on Windows? Does it support the options?
	if err := cmd(B2D.SSH,
		"-o", "StrictHostKeyChecking=no",
		"-o", "UserKnownHostsFile=/dev/null",
		"-p", fmt.Sprintf("%d", B2D.SSHPort),
		"docker@localhost",
	); err != nil {
		logf("%s", err)
		return 1
	}
	return 0
}

// Download the boot2docker ISO image.
func cmdDownload() int {
	logf("Downloading boot2docker ISO image...")
	url := "https://api.github.com/repos/boot2docker/boot2docker/releases"
	tag, err := getLatestReleaseName(url)
	if err != nil {
		logf("Failed to get latest release: %s", err)
		return 1
	}
	logf("Latest release is %s", tag)

	url = fmt.Sprintf("https://github.com/boot2docker/boot2docker/releases/download/%s/boot2docker.iso", tag)
	if err := download(B2D.ISO, url); err != nil {
		logf("Failed to download ISO image: %s", err)
		return 1
	}
	return 0
}
<commit_msg>updates cmdInit() to check for B2D.VMDK<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"runtime"
	"time"

	vbx "github.com/boot2docker/boot2docker-cli/virtualbox"
)

// Initialize the boot2docker VM from scratch.
func cmdInit() int {
	// TODO(@riobard) break up this command into multiple stages

	if ping(fmt.Sprintf("localhost:%d", B2D.DockerPort)) {
		logf("DOCKER_PORT=%d on localhost is occupied. Please choose another one.", B2D.DockerPort)
		return 1
	}

	if ping(fmt.Sprintf("localhost:%d", B2D.SSHPort)) {
		logf("SSH_PORT=%d on localhost is occupied. Please choose another one.", B2D.SSHPort)
		return 1
	}

	if _, err := os.Stat(B2D.ISO); err != nil {
		if !os.IsNotExist(err) {
			logf("Failed to open ISO image %q: %s", B2D.ISO, err)
			return 1
		}

		if exitcode := cmdDownload(); exitcode != 0 {
			return exitcode
		}
	}

	logf("Creating VM %s...", B2D.VM)
	m, err := vbx.CreateMachine(B2D.VM, "")
	if err != nil {
		logf("Failed to create VM %q: %s", B2D.VM, err)
		return 1
	}

	logf("Apply interim patch to VM %s (https://www.virtualbox.org/ticket/12748)", B2D.VM)
	if err := vbx.SetExtra(B2D.VM, "VBoxInternal/CPUM/EnableHVP", "1"); err != nil {
		logf("Failed to patch vm: %s", err)
		return 1
	}

	m.OSType = "Linux26_64"
	m.CPUs = uint(runtime.NumCPU())
	m.Memory = B2D.Memory

	m.Flag |= vbx.F_pae
	m.Flag |= vbx.F_longmode // important: use x86-64 processor
	m.Flag |= vbx.F_rtcuseutc
	m.Flag |= vbx.F_acpi
	m.Flag |= vbx.F_ioapic
	m.Flag |= vbx.F_hpet
	m.Flag |= vbx.F_hwvirtex
	m.Flag |= vbx.F_vtxvpid
	m.Flag |= vbx.F_largepages
	m.Flag |= vbx.F_nestedpaging

	m.BootOrder = []string{"dvd"}
	if err := m.Modify(); err != nil {
		logf("Failed to modify VM %q: %s", B2D.VM, err)
		return 1
	}

	logf("Setting NIC #1 to use NAT network...")
	if err := m.SetNIC(1, vbx.NIC{Network: vbx.NICNetNAT, Hardware: vbx.VirtIO}); err != nil {
		logf("Failed to add network interface to VM %q: %s", B2D.VM, err)
		return 1
	}

	pfRules := map[string]vbx.PFRule{
		"ssh":    vbx.PFRule{Proto: vbx.PFTCP, HostIP: net.ParseIP("127.0.0.1"), HostPort: B2D.SSHPort, GuestPort: 22},
		"docker": vbx.PFRule{Proto: vbx.PFTCP, HostIP: net.ParseIP("127.0.0.1"), HostPort: B2D.DockerPort, GuestPort: 4243},
	}

	for name, rule := range pfRules {
		if err := m.AddNATPF(1, name, rule); err != nil {
			logf("Failed to add port forwarding to VM %q: %s", B2D.VM, err)
			return 1
		}
		logf("Port forwarding [%s] %s", name, rule)
	}

	hostIFName, err := getHostOnlyNetworkInterface()
	if err != nil {
		logf("Failed to create host-only network interface: %s", err)
		return 1
	}

	logf("Setting NIC #2 to use host-only network %q...", hostIFName)
	if err := m.SetNIC(2, vbx.NIC{Network: vbx.NICNetHostonly, Hardware: vbx.VirtIO, HostonlyAdapter: hostIFName}); err != nil {
		logf("Failed to add network interface to VM %q: %s", B2D.VM, err)
		return 1
	}

	logf("Setting VM storage...")
	if err := m.AddStorageCtl("SATA", vbx.StorageController{SysBus: vbx.SysBusSATA, HostIOCache: true, Bootable: true}); err != nil {
		logf("Failed to add storage controller to VM %q: %s", B2D.VM, err)
		return 1
	}

	if err := m.AttachStorage("SATA", vbx.StorageMedium{Port: 0, Device: 0, DriveType: vbx.DriveDVD, Medium: B2D.ISO}); err != nil {
		logf("Failed to attach ISO image %q: %s", B2D.ISO, err)
		return 1
	}

	diskImg := filepath.Join(m.BaseFolder, fmt.Sprintf("%s.vmdk", B2D.VM))

	if _, err := os.Stat(diskImg); err != nil {
		if !os.IsNotExist(err) {
			logf("Failed to open disk image %q: %s", diskImg, err)
			return 1
		}

    if B2D.VMDK != "" {
      logf("Using %v as base VMDK", B2D.VMDK)
      if err := copyDiskImage(diskImg, B2D.VMDK); err != nil {
        logf("Failed to copy disk image %v from %v: %s", diskImg, B2D.VMDK, err)
        return 1
      }
    } else {
      if err := makeDiskImage(diskImg, B2D.DiskSize); err != nil {
        logf("Failed to create disk image %q: %s", diskImg, err)
        return 1
      }
    }
	}

	if err := m.AttachStorage("SATA", vbx.StorageMedium{Port: 1, Device: 0, DriveType: vbx.DriveHDD, Medium: diskImg}); err != nil {
		logf("Failed to attach disk image %q: %s", diskImg, err)
		return 1
	}

	logf("Done. Type `%s up` to start the VM.", os.Args[0])
	return 0
}

// Bring up the VM from all possible states.
func cmdUp() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Start(); err != nil {
		logf("Failed to start machine %q: %s", B2D.VM, err)
		return 1
	}

	logf("Waiting for SSH server to start...")
	addr := fmt.Sprintf("localhost:%d", B2D.SSHPort)
	const n = 10
	// Try to connect to the SSH 10 times at 3 sec interval before giving up.
	if err := read(addr, n, 3*time.Second); err != nil {
		logf("Failed to connect to SSH port at %s after %d attempts. Last error: %v", addr, n, err)
		return 1
	}

	logf("Started.")

	switch runtime.GOOS {
	case "windows":
		logf("Docker client does not run on Windows for now. Please use")
		logf("    %s ssh", os.Args[0])
		logf("to SSH into the VM instead.")
	default:
		// Check if $DOCKER_HOST ENV var is properly configured.
		if os.Getenv("DOCKER_HOST") != fmt.Sprintf("tcp://localhost:%d", B2D.DockerPort) {
			logf("To connect the Docker client to the Docker daemon, please set:")
			logf("    export DOCKER_HOST=tcp://localhost:%d", B2D.DockerPort)
		}
	}
	return 0
}

// Suspend and save the current state of VM on disk.
func cmdSave() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Save(); err != nil {
		logf("Failed to save machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Gracefully stop the VM by sending ACPI shutdown signal.
func cmdStop() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Stop(); err != nil {
		logf("Failed to stop machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Forcefully power off the VM (equivalent to unplug power). Might corrupt disk
// image.
func cmdPoweroff() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Poweroff(); err != nil {
		logf("Failed to poweroff machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Gracefully stop and then start the VM.
func cmdRestart() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Restart(); err != nil {
		logf("Failed to restart machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Forcefully reset (equivalent to cold boot) the VM. Might corrupt disk image.
func cmdReset() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Reset(); err != nil {
		logf("Failed to reset machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Delete the VM and associated disk image.
func cmdDelete() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		if err == vbx.ErrMachineNotExist {
			logf("Machine %q does not exist.", B2D.VM)
			return 0
		}
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := m.Delete(); err != nil {
		logf("Failed to delete machine %q: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Show detailed info of the VM.
func cmdInfo() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	if err := json.NewEncoder(os.Stdout).Encode(m); err != nil {
		logf("Failed to encode machine %q info: %s", B2D.VM, err)
		return 1
	}
	return 0
}

// Show the current state of the VM.
func cmdStatus() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}
	fmt.Println(m.State)
	return 0
}

// Call the external SSH command to login into boot2docker VM.
func cmdSSH() int {
	m, err := vbx.GetMachine(B2D.VM)
	if err != nil {
		logf("Failed to get machine %q: %s", B2D.VM, err)
		return 2
	}

	if m.State != vbx.Running {
		logf("VM %q is not running.", B2D.VM)
		return 1
	}

	// TODO What SSH client is used on Windows? Does it support the options?
	if err := cmd(B2D.SSH,
		"-o", "StrictHostKeyChecking=no",
		"-o", "UserKnownHostsFile=/dev/null",
		"-p", fmt.Sprintf("%d", B2D.SSHPort),
		"docker@localhost",
	); err != nil {
		logf("%s", err)
		return 1
	}
	return 0
}

// Download the boot2docker ISO image.
func cmdDownload() int {
	logf("Downloading boot2docker ISO image...")
	url := "https://api.github.com/repos/boot2docker/boot2docker/releases"
	tag, err := getLatestReleaseName(url)
	if err != nil {
		logf("Failed to get latest release: %s", err)
		return 1
	}
	logf("Latest release is %s", tag)

	url = fmt.Sprintf("https://github.com/boot2docker/boot2docker/releases/download/%s/boot2docker.iso", tag)
	if err := download(B2D.ISO, url); err != nil {
		logf("Failed to download ISO image: %s", err)
		return 1
	}
	return 0
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/ying32/govcl/vcl"
	"github.com/ying32/govcl/vcl/exts/macapp"
	_ "github.com/ying32/govcl/vcl/exts/winappres"
)

func main() {
	macapp.RunWithMacOSApp()
	vcl.Application.Initialize()
	vcl.Application.CreateForm(&MainForm)
	vcl.Application.Run()
}
<commit_msg>update miniwebview/main.go<commit_after>package main

import (
	"github.com/ying32/govcl/vcl"
	_ "github.com/ying32/govcl/vcl/exts/macapp"
	_ "github.com/ying32/govcl/vcl/exts/winappres"
)

func main() {
	vcl.Application.Initialize()
	vcl.Application.CreateForm(&MainForm)
	vcl.Application.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/fhs/gompd/mpd"
	"strconv"
	"sync"
	"time"
)

type mpcMessageType int

const (
	syncLibrary mpcMessageType = iota
	syncPlaylist
	syncCurrent
	prev
	play
	next
	ping
	nop
)

type mpcMessage struct {
	request mpcMessageType
	err     chan error
}

/*Dial Connects to mpd server.*/
func Dial(network, addr string) (*Player, error) {
	p := new(Player)
	p.mutex = new(sync.Mutex)
	p.daemonStop = make(chan bool)
	p.daemonRequest = make(chan *mpcMessage)
	p.network = network
	p.addr = addr
	return p, p.start()
}

/*Player represents mpd control interface.*/
type Player struct {
	network          string
	addr             string
	mpc              MpdClient
	watcher          mpd.Watcher
	daemonStop       chan bool
	daemonRequest    chan *mpcMessage
	mutex            *sync.Mutex
	current          mpd.Attrs
	currentModified  time.Time
	status           PlayerStatus
	comments         mpd.Attrs
	commentsModified time.Time
	library          []mpd.Attrs
	libraryModified  time.Time
	playlist         []mpd.Attrs
	playlistModified time.Time
}

/*PlayerStatus represents mpd status.*/
type PlayerStatus struct {
	Volume       int     `json:"volume"`
	Repeat       bool    `json:"repeat"`
	Random       bool    `json:"random"`
	Single       bool    `json:"single"`
	Consume      bool    `json:"consume"`
	State        string  `json:"state"`
	SongPos      int     `json:"song_pos"`
	SongElapsed  float32 `json:"song_elapsed"`
	SongLength   int     `json:"song_length"`
	LastModified float32 `json:"last_modified"`
}

/*MpdClient represents mpd.Client for Player.*/
type MpdClient interface {
	Play(int) error
	Pause(bool) error
	Previous() error
	Next() error
	Ping() error
	Close() error
	ReadComments(string) (mpd.Attrs, error)
	CurrentSong() (mpd.Attrs, error)
	Status() (mpd.Attrs, error)
	ListAllInfo(string) ([]mpd.Attrs, error)
	PlaylistInfo(int, int) ([]mpd.Attrs, error)
}

/*Close mpd connection.*/
func (p *Player) Close() error {
	p.daemonStop <- true
	p.mpc.Close()
	return p.watcher.Close()
}

/*Comments returns mpd current song raw meta data.*/
func (p *Player) Comments() (mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.comments, p.commentsModified
}

/*Current returns mpd current song data.*/
func (p *Player) Current() (mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.current, p.currentModified
}

/*Status returns mpd current song data.*/
func (p *Player) Status() (PlayerStatus, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.status, p.currentModified
}

/*Library returns mpd library song data list.*/
func (p *Player) Library() ([]mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.library, p.libraryModified
}

/*Playlist returns mpd playlist song data list.*/
func (p *Player) Playlist() ([]mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.playlist, p.playlistModified
}

/*Pause song.*/
func (p *Player) Pause() error {
	return p.mpc.Pause(true)
}

/*Play or resume song.*/
func (p *Player) Play() error {
	return p.request(play)
}

/*Prev song.*/
func (p *Player) Prev() error {
	return p.request(prev)
}

/*Next song.*/
func (p *Player) Next() error {
	return p.request(next)
}

/*Nop ping daemon goroutine.*/
func (p *Player) Nop() {
	p.request(nop)
}

func (p *Player) start() (err error) {
	err = p.connect()
	if err != nil {
		return err
	}
	err = p.syncLibrary()
	if err != nil {
		p.Close()
		return
	}
	err = p.syncPlaylist()
	if err != nil {
		p.Close()
		return
	}
	err = p.syncCurrent()
	if err != nil {
		p.Close()
		return
	}
	go p.daemon()
	go p.watch()
	go p.ping()
	return
}

func (p *Player) daemon() {
loop:
	for {
		select {
		case <-p.daemonStop:
			break loop
		case m := <-p.daemonRequest:
			switch m.request {
			case prev:
				m.err <- p.mpc.Previous()
			case play:
				m.err <- p.mpc.Play(-1)
			case next:
				m.err <- p.mpc.Next()
			case syncLibrary:
				m.err <- p.syncLibrary()
			case syncPlaylist:
				m.err <- p.syncPlaylist()
			case syncCurrent:
				m.err <- p.syncCurrent()
			case ping:
				m.err <- p.mpc.Ping()
			case nop:
				m.err <- nil
			}
		}
	}
}

func (p *Player) ping() {
	for {
		time.Sleep(1)
		p.request(ping)
	}
}

func (p *Player) watch() {
	for subsystem := range p.watcher.Event {
		switch subsystem {
		case "database":
			p.request(syncLibrary)
		case "playlist":
			p.request(syncPlaylist)
		case "player":
			p.request(syncCurrent)
		}
	}
}

func (p *Player) reconnect() error {
	p.watcher.Close()
	p.mpc.Close()
	return p.connect()
}

func (p *Player) connect() error {
	mpc, err := mpd.Dial(p.network, p.addr)
	if err != nil {
		return err
	}
	p.mpc = mpc
	watcher, err := mpd.NewWatcher(p.network, p.addr, "")
	if err != nil {
		mpc.Close()
		return err
	}
	p.watcher = *watcher
	return nil
}

func (p *Player) request(req mpcMessageType) error {
	r := new(mpcMessage)
	r.request = req
	r.err = make(chan error)
	p.daemonRequest <- r
	return <-r.err
}

func convStatus(a mpd.Attrs, s *PlayerStatus) {
	elapsed, err := strconv.ParseFloat(a["elapsed"], 64)
	if err != nil {
		elapsed = 0.0
	}
	volume, err := strconv.Atoi(a["volume"])
	if err != nil {
		volume = -1
	}
	songpos, err := strconv.Atoi(a["song"])
	if err != nil {
		songpos = 0
	}
	state := a["state"]
	if state == "" {
		state = "stopped"
	}
	s.Volume = volume
	s.Repeat = a["repeat"] == "1"
	s.Random = a["random"] == "1"
	s.Single = a["single"] == "1"
	s.Consume = a["consume"] == "1"
	s.State = state
	s.SongPos = songpos
	s.SongElapsed = float32(elapsed)
	millisec := float32(time.Now().UnixNano()/int64(time.Millisecond)) / 1000.0
	s.LastModified = millisec
}

func (p *Player) syncCurrent() error {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	song, err := p.mpc.CurrentSong()
	if err != nil {
		return err
	}
	p.currentModified = time.Now()
	status, err := p.mpc.Status()
	if err != nil {
		return err
	}
	convStatus(status, &p.status)
	songlength, err := strconv.Atoi(song["Time"])
	if err != nil {
		songlength = 0
	}
	p.status.SongLength = songlength
	if p.comments == nil || p.current["file"] != song["file"] {
		comments, err := p.mpc.ReadComments(song["file"])
		if err != nil {
			return err
		}
		p.commentsModified = time.Now()
		p.comments = comments
	}

	p.current = song
	return nil
}

func (p *Player) syncLibrary() error {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	library, err := p.mpc.ListAllInfo("/")
	if err != nil {
		return err
	}
	p.library = library
	p.libraryModified = time.Now()
	return nil
}

func (p *Player) syncPlaylist() error {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	playlist, err := p.mpc.PlaylistInfo(-1, -1)
	if err != nil {
		return err
	}
	p.playlist = playlist
	p.playlistModified = time.Now()
	return nil
}
<commit_msg>fix lastmodified to int64<commit_after>package main

import (
	"github.com/fhs/gompd/mpd"
	"strconv"
	"sync"
	"time"
)

type mpcMessageType int

const (
	syncLibrary mpcMessageType = iota
	syncPlaylist
	syncCurrent
	prev
	play
	next
	ping
	nop
)

type mpcMessage struct {
	request mpcMessageType
	err     chan error
}

/*Dial Connects to mpd server.*/
func Dial(network, addr string) (*Player, error) {
	p := new(Player)
	p.mutex = new(sync.Mutex)
	p.daemonStop = make(chan bool)
	p.daemonRequest = make(chan *mpcMessage)
	p.network = network
	p.addr = addr
	return p, p.start()
}

/*Player represents mpd control interface.*/
type Player struct {
	network          string
	addr             string
	mpc              MpdClient
	watcher          mpd.Watcher
	daemonStop       chan bool
	daemonRequest    chan *mpcMessage
	mutex            *sync.Mutex
	current          mpd.Attrs
	currentModified  time.Time
	status           PlayerStatus
	comments         mpd.Attrs
	commentsModified time.Time
	library          []mpd.Attrs
	libraryModified  time.Time
	playlist         []mpd.Attrs
	playlistModified time.Time
}

/*PlayerStatus represents mpd status.*/
type PlayerStatus struct {
	Volume       int     `json:"volume"`
	Repeat       bool    `json:"repeat"`
	Random       bool    `json:"random"`
	Single       bool    `json:"single"`
	Consume      bool    `json:"consume"`
	State        string  `json:"state"`
	SongPos      int     `json:"song_pos"`
	SongElapsed  float32 `json:"song_elapsed"`
	SongLength   int     `json:"song_length"`
	LastModified int64   `json:"last_modified"`
}

/*MpdClient represents mpd.Client for Player.*/
type MpdClient interface {
	Play(int) error
	Pause(bool) error
	Previous() error
	Next() error
	Ping() error
	Close() error
	ReadComments(string) (mpd.Attrs, error)
	CurrentSong() (mpd.Attrs, error)
	Status() (mpd.Attrs, error)
	ListAllInfo(string) ([]mpd.Attrs, error)
	PlaylistInfo(int, int) ([]mpd.Attrs, error)
}

/*Close mpd connection.*/
func (p *Player) Close() error {
	p.daemonStop <- true
	p.mpc.Close()
	return p.watcher.Close()
}

/*Comments returns mpd current song raw meta data.*/
func (p *Player) Comments() (mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.comments, p.commentsModified
}

/*Current returns mpd current song data.*/
func (p *Player) Current() (mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.current, p.currentModified
}

/*Status returns mpd current song data.*/
func (p *Player) Status() (PlayerStatus, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.status, p.currentModified
}

/*Library returns mpd library song data list.*/
func (p *Player) Library() ([]mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.library, p.libraryModified
}

/*Playlist returns mpd playlist song data list.*/
func (p *Player) Playlist() ([]mpd.Attrs, time.Time) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.playlist, p.playlistModified
}

/*Pause song.*/
func (p *Player) Pause() error {
	return p.mpc.Pause(true)
}

/*Play or resume song.*/
func (p *Player) Play() error {
	return p.request(play)
}

/*Prev song.*/
func (p *Player) Prev() error {
	return p.request(prev)
}

/*Next song.*/
func (p *Player) Next() error {
	return p.request(next)
}

/*Nop ping daemon goroutine.*/
func (p *Player) Nop() {
	p.request(nop)
}

func (p *Player) start() (err error) {
	err = p.connect()
	if err != nil {
		return err
	}
	err = p.syncLibrary()
	if err != nil {
		p.Close()
		return
	}
	err = p.syncPlaylist()
	if err != nil {
		p.Close()
		return
	}
	err = p.syncCurrent()
	if err != nil {
		p.Close()
		return
	}
	go p.daemon()
	go p.watch()
	go p.ping()
	return
}

func (p *Player) daemon() {
loop:
	for {
		select {
		case <-p.daemonStop:
			break loop
		case m := <-p.daemonRequest:
			switch m.request {
			case prev:
				m.err <- p.mpc.Previous()
			case play:
				m.err <- p.mpc.Play(-1)
			case next:
				m.err <- p.mpc.Next()
			case syncLibrary:
				m.err <- p.syncLibrary()
			case syncPlaylist:
				m.err <- p.syncPlaylist()
			case syncCurrent:
				m.err <- p.syncCurrent()
			case ping:
				m.err <- p.mpc.Ping()
			case nop:
				m.err <- nil
			}
		}
	}
}

func (p *Player) ping() {
	for {
		time.Sleep(1)
		p.request(ping)
	}
}

func (p *Player) watch() {
	for subsystem := range p.watcher.Event {
		switch subsystem {
		case "database":
			p.request(syncLibrary)
		case "playlist":
			p.request(syncPlaylist)
		case "player":
			p.request(syncCurrent)
		}
	}
}

func (p *Player) reconnect() error {
	p.watcher.Close()
	p.mpc.Close()
	return p.connect()
}

func (p *Player) connect() error {
	mpc, err := mpd.Dial(p.network, p.addr)
	if err != nil {
		return err
	}
	p.mpc = mpc
	watcher, err := mpd.NewWatcher(p.network, p.addr, "")
	if err != nil {
		mpc.Close()
		return err
	}
	p.watcher = *watcher
	return nil
}

func (p *Player) request(req mpcMessageType) error {
	r := new(mpcMessage)
	r.request = req
	r.err = make(chan error)
	p.daemonRequest <- r
	return <-r.err
}

func convStatus(a mpd.Attrs, s *PlayerStatus) {
	elapsed, err := strconv.ParseFloat(a["elapsed"], 64)
	if err != nil {
		elapsed = 0.0
	}
	volume, err := strconv.Atoi(a["volume"])
	if err != nil {
		volume = -1
	}
	songpos, err := strconv.Atoi(a["song"])
	if err != nil {
		songpos = 0
	}
	state := a["state"]
	if state == "" {
		state = "stopped"
	}
	s.Volume = volume
	s.Repeat = a["repeat"] == "1"
	s.Random = a["random"] == "1"
	s.Single = a["single"] == "1"
	s.Consume = a["consume"] == "1"
	s.State = state
	s.SongPos = songpos
	s.SongElapsed = float32(elapsed)
	s.LastModified = time.Now().Unix()
}

func (p *Player) syncCurrent() error {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	song, err := p.mpc.CurrentSong()
	if err != nil {
		return err
	}
	p.currentModified = time.Now()
	status, err := p.mpc.Status()
	if err != nil {
		return err
	}
	convStatus(status, &p.status)
	songlength, err := strconv.Atoi(song["Time"])
	if err != nil {
		songlength = 0
	}
	p.status.SongLength = songlength
	if p.comments == nil || p.current["file"] != song["file"] {
		comments, err := p.mpc.ReadComments(song["file"])
		if err != nil {
			return err
		}
		p.commentsModified = time.Now()
		p.comments = comments
	}

	p.current = song
	return nil
}

func (p *Player) syncLibrary() error {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	library, err := p.mpc.ListAllInfo("/")
	if err != nil {
		return err
	}
	p.library = library
	p.libraryModified = time.Now()
	return nil
}

func (p *Player) syncPlaylist() error {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	playlist, err := p.mpc.PlaylistInfo(-1, -1)
	if err != nil {
		return err
	}
	p.playlist = playlist
	p.playlistModified = time.Now()
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Fix the logger usage (#604)<commit_after><|endoftext|>


<commit_before>package main

import "testing"

func Test(t *testing.T) {
	main()
}

<commit_msg>gofmt<commit_after>package main

import "testing"

func Test(t *testing.T) {
	main()
}
<|endoftext|>


<commit_before>// Copyright 2015 Luke Shumaker

package httpentity

import (
	"httpentity/negotiate"
	"mime"
	"net/url"
	"path"
	"strings"
)

func normalizeURL(u1 *url.URL) (u *url.URL, mimetype string) {
	u, _ = u1.Parse("") // normalize
	// the file extension overrides the Accept: header
	if ext := path.Ext(u.Path); ext != "" {
		mimetype = mime.TypeByExtension(ext)
		u.Path = strings.TrimSuffix(u.Path, ext)
	}
	// add a trailing slash if there isn't one (so that relative
	// child URLs don't go to the parent)
	if !strings.HasSuffix(u.Path, "/") {
		u.Path = u.Path + "/"
	}
	return
}

// Init initializes the hidden fields; should be called before any other
// method.
func (r Router) Init() *Router {
	r.initHandlers()
	return &r
}

// assumes that the url has already been passed to normalizeURL()
func (r *Router) finish(req Request, res *Response) {
	// generate a 500 error if we paniced
	if err := recover(); err != nil {
		*res = r.responseServerError(err)
	}
	// figure out the content type of the response
	if res.Entity != nil && res.Headers.Get("Content-Type") == "" {
		encoders := res.Entity.Encoders()
		mimetypes := encoders2mimetypes(encoders)
		accept := req.Headers.Get("Accept")
		if len(encoders) > 1 && accept == "" {
			*res = r.responseMultipleChoices(req.URL, mimetypes)
		} else {
			options, err := negotiate.NegotiateContentType(&accept, mimetypes)
			if err != nil {
				*res = r.responseBadRequest(err)
			} else {
				switch len(options) {
				case 0:
					*res = r.responseNotAcceptable(req.URL, mimetypes)
				case 1:
					//res.Headers.Set("Content-Type", mimetype+"; charset=utf-8")
					res.Headers.Set("Content-Type", mimetypes[0])
				default:
					*res = r.responseMultipleChoices(req.URL, mimetypes)
				}
			}
		}
	}
}
<commit_msg>httpentity: fix up content type negotiation<commit_after>// Copyright 2015 Luke Shumaker

package httpentity

import (
	"httpentity/negotiate"
	"mime"
	"net/url"
	"net/http"
	"path"
	"strings"
)

func normalizeURL(u1 *url.URL) (u *url.URL, mimetype string) {
	u, _ = u1.Parse("") // normalize
	// the file extension overrides the Accept: header
	if ext := path.Ext(u.Path); ext != "" {
		mimetype = mime.TypeByExtension(ext)
		u.Path = strings.TrimSuffix(u.Path, ext)
	}
	// add a trailing slash if there isn't one (so that relative
	// child URLs don't go to the parent)
	if !strings.HasSuffix(u.Path, "/") {
		u.Path = u.Path + "/"
	}
	return
}

// Init initializes the hidden fields; should be called before any other
// method.
func (r Router) Init() *Router {
	r.initHandlers()
	return &r
}

// assumes that the url has already been passed to normalizeURL()
func (r *Router) finish(req Request, res *Response) {
	// generate a 500 error if we paniced
	if err := recover(); err != nil {
		*res = r.responseServerError(err)
	}
	// figure out the content type of the response
	if res.Entity != nil && res.Headers.Get("Content-Type") == "" {
		encoders := res.Entity.Encoders()
		mimetypes := encoders2mimetypes(encoders)
		acceptStrs, haveAccept := req.Headers[http.CanonicalHeaderKey("Accept")]
		var accept *string
		if haveAccept && len(acceptStrs) > 0 {
			accept = &acceptStrs[0]
		}
		options, err := negotiate.NegotiateContentType(accept, mimetypes)
		if err != nil {
			*res = r.responseBadRequest(err)
		} else {
			switch len(options) {
			case 0:
				*res = r.responseNotAcceptable(req.URL, mimetypes)
			case 1:
				//res.Headers.Set("Content-Type", options[0]+"; charset=utf-8")
				res.Headers.Set("Content-Type", options[0])
				return
			default:
				*res = r.responseMultipleChoices(req.URL, mimetypes)
			}
		}
		// If we make it here, we're either serving a
		// BadRequest (because parsing Accept failed),
		// NotAcceptable, or MultipleChoices, but don't know
		// what content type to send the message as!  We can't
		// just recurse because we don't want the status code
		// to change.
		encoders = res.Entity.Encoders()
		mimetypes = encoders2mimetypes(encoders)
		options, err = negotiate.NegotiateContentType(accept, mimetypes)
		switch len(options) {
		case 0:
			// Just pick one
			res.Headers.Set("Content-Type", mimetypes[0])
		case 1:
			res.Headers.Set("Content-Type", options[0])
		default:
			// Just pick one
			res.Headers.Set("Content-Type", options[0])
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"sync"

	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/pkg/stream"
)

var testMode = false

type DiscoverdServiceCache interface {
	Addrs() []string
	Close() error
}

func NewDiscoverdServiceCache(s discoverd.Service) (DiscoverdServiceCache, error) {
	d := &discoverdServiceCache{addrs: make(map[string]struct{})}
	return d, d.start(s)
}

type discoverdServiceCache struct {
	stream stream.Stream

	sync.RWMutex
	addrs map[string]struct{}

	// used by the test suite
	watchCh chan *discoverd.Event
}

func (d *discoverdServiceCache) start(s discoverd.Service) (err error) {
	events := make(chan *discoverd.Event)
	d.stream, err = s.Watch(events)
	if err != nil {
		return err
	}
	current := make(chan error)
	go func() {
		for event := range events {
			switch event.Kind {
			case discoverd.EventKindUp, discoverd.EventKindUpdate:
				d.Lock()
				d.addrs[event.Instance.Addr] = struct{}{}
				d.Unlock()
			case discoverd.EventKindDown:
				d.Lock()
				delete(d.addrs, event.Instance.Addr)
				d.Unlock()
			case discoverd.EventKindCurrent:
				if current != nil {
					current <- nil
					current = nil
				}
			}
			if testMode {
				d.Lock()
				if d.watchCh != nil {
					d.watchCh <- event
				}
				d.Unlock()
			}
		}
		if current != nil {
			current <- d.stream.Err()
		}
		// TODO: handle discoverd disconnection
	}()
	return <-current
}

func (d *discoverdServiceCache) Close() error {
	return d.stream.Close()
}

func (d *discoverdServiceCache) Addrs() []string {
	d.RLock()
	defer d.RUnlock()
	res := make([]string, 0, len(d.addrs))
	for addr := range d.addrs {
		res = append(res, addr)
	}
	return res
}

// This method is only used by the test suite
func (d *discoverdServiceCache) watch(current bool) chan *discoverd.Event {
	d.Lock()
	d.watchCh = make(chan *discoverd.Event)
	go func() {
		if current {
			for addr := range d.addrs {
				d.watchCh <- &discoverd.Event{
					Kind:     discoverd.EventKindUp,
					Instance: &discoverd.Instance{Addr: addr},
				}
			}
		}
		d.Unlock()
	}()
	return d.watchCh
}

func (d *discoverdServiceCache) unwatch(ch chan *discoverd.Event) {
	go func() {
		for range ch {
		}
	}()
	d.Lock()
	close(d.watchCh)
	d.watchCh = nil
	d.Unlock()
}
<commit_msg>router: Reconnect to discoverd<commit_after>package main

import (
	"sync"
	"time"

	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/pkg/attempt"
	"github.com/flynn/flynn/pkg/stream"
)

var testMode = false

type DiscoverdServiceCache interface {
	Addrs() []string
	Close() error
}

func NewDiscoverdServiceCache(s discoverd.Service) (DiscoverdServiceCache, error) {
	d := &discoverdServiceCache{
		addrs: make(map[string]struct{}),
		stop:  make(chan struct{}),
	}
	return d, d.start(s)
}

type discoverdServiceCache struct {
	stream stream.Stream

	sync.RWMutex
	addrs map[string]struct{}

	// used by the test suite
	watchCh chan *discoverd.Event

	stop chan struct{}
}

var connectAttempts = attempt.Strategy{
	Total: 10 * time.Minute,
	Delay: 500 * time.Millisecond,
}

func (d *discoverdServiceCache) start(s discoverd.Service) (err error) {
	// use a function to create the watcher so we can reconnect if it closes
	// unexpectedly (ideally the discoverd client would use a ResumingStream
	// but service events do not yet support it).
	var events chan *discoverd.Event
	connect := func() (err error) {
		events = make(chan *discoverd.Event)
		d.stream, err = s.Watch(events)
		return
	}
	if err := connect(); err != nil {
		return err
	}
	var once sync.Once
	current := make(chan error)
	go func() {
		for {
			select {
			case <-d.stop:
				return
			case event, ok := <-events:
				if !ok {
					if err := connectAttempts.Run(connect); err != nil {
						once.Do(func() { current <- err })
						return
					}
					continue
				}

				switch event.Kind {
				case discoverd.EventKindUp, discoverd.EventKindUpdate:
					d.Lock()
					d.addrs[event.Instance.Addr] = struct{}{}
					d.Unlock()
				case discoverd.EventKindDown:
					d.Lock()
					delete(d.addrs, event.Instance.Addr)
					d.Unlock()
				case discoverd.EventKindCurrent:
					once.Do(func() { current <- nil })
				}
				if testMode {
					d.Lock()
					if d.watchCh != nil {
						d.watchCh <- event
					}
					d.Unlock()
				}
			}
		}
	}()
	return <-current
}

func (d *discoverdServiceCache) Close() error {
	close(d.stop)
	return d.stream.Close()
}

func (d *discoverdServiceCache) Addrs() []string {
	d.RLock()
	defer d.RUnlock()
	res := make([]string, 0, len(d.addrs))
	for addr := range d.addrs {
		res = append(res, addr)
	}
	return res
}

// This method is only used by the test suite
func (d *discoverdServiceCache) watch(current bool) chan *discoverd.Event {
	d.Lock()
	d.watchCh = make(chan *discoverd.Event)
	go func() {
		if current {
			for addr := range d.addrs {
				d.watchCh <- &discoverd.Event{
					Kind:     discoverd.EventKindUp,
					Instance: &discoverd.Instance{Addr: addr},
				}
			}
		}
		d.Unlock()
	}()
	return d.watchCh
}

func (d *discoverdServiceCache) unwatch(ch chan *discoverd.Event) {
	go func() {
		for range ch {
		}
	}()
	d.Lock()
	close(d.watchCh)
	d.watchCh = nil
	d.Unlock()
}
<|endoftext|>


<commit_before>package dbshield

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"syscall"
	"testing"
	"time"

	"github.com/boltdb/bolt"
	"github.com/nim4/DBShield/dbshield/config"
	"github.com/nim4/DBShield/dbshield/sql"
	"github.com/nim4/DBShield/dbshield/training"
)

func TestMain(m *testing.M) {
	os.Chdir("../")
	m.Run()
}

func TestSetConfigFile(t *testing.T) {
	err := SetConfigFile("Invalid.yml")
	if err == nil {
		t.Error("Expected error")
	}
}

func TestCheck(t *testing.T) {
	SetConfigFile("conf/dbshield.yml")
	err := Check()
	if err != nil {
		t.Error("Got error", err)
	}
}

func TestPostConfig(t *testing.T) {
	SetConfigFile("conf/dbshield.yml")
	config.Config.DBType = "Invalid"
	err := postConfig()
	if err == nil {
		t.Error("Expected error")
	}

	config.Config.ListenPort = 0
	config.Config.DBType = "mysql"
	err = postConfig()
	if err != nil {
		t.Error("Expected nil got ", err)
	}

	config.Config.TLSCertificate = os.TempDir()
	err = postConfig()
	if err == nil {
		t.Error("Expected error")
	}
}

func TestEveryThing(t *testing.T) {
	closeHandlers()
	SetConfigFile("conf/dbshield.yml")
	//It should fail if port is already open
	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.Config.ListenIP, config.Config.ListenPort))
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()

	err = mainListner()
	if err == nil {
		t.Error("Expected error")
	}

	go func() {
		timer := time.NewTimer(time.Second * 2)
		<-timer.C
		syscall.Kill(syscall.Getpid(), syscall.SIGINT)
	}()
	err = Start()
	if err != nil {
		t.Error("Got error", err)
	}
	file, _ := ioutil.TempFile(os.TempDir(), "tempDB")
	defer os.Remove(file.Name())
	training.DBCon, _ = bolt.Open(file.Name(), 0600, nil)
	training.DBCon.Update(func(tx *bolt.Tx) error {
		tx.CreateBucket([]byte("pattern"))
		tx.CreateBucket([]byte("abnormal"))
		tx.CreateBucket([]byte("state"))
		return nil
	})
	c := sql.QueryContext{
		Query:    []byte("select * from test;"),
		Database: []byte("test"),
		User:     []byte("test"),
		Client:   []byte("127.0.0.1"),
		Time:     time.Now(),
	}
	training.CheckQuery(c)
	err = training.AddToTrainingSet(c)
	if err != nil {
		t.Error("Got error", err)
	}

	Patterns()
	Abnormals()

	//Test without bucket
	tmpCon := training.DBCon
	defer func() {
		training.DBCon = tmpCon
	}()
	tmpfile, err := ioutil.TempFile("", "testdb")
	if err != nil {
		panic(err)
	}
	defer tmpfile.Close()
	path := tmpfile.Name()
	training.DBCon, err = bolt.Open(path, 0600, nil)
	if err != nil {
		panic(err)
	}
	Patterns()
	Abnormals()
}
<commit_msg>Fix syscall.Kill - removed test in windows<commit_after>// +build !windows

package dbshield

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"syscall"
	"testing"
	"time"

	"github.com/boltdb/bolt"
	"github.com/nim4/DBShield/dbshield/config"
	"github.com/nim4/DBShield/dbshield/sql"
	"github.com/nim4/DBShield/dbshield/training"
)

func TestMain(m *testing.M) {
	os.Chdir("../")
	m.Run()
}

func TestSetConfigFile(t *testing.T) {
	err := SetConfigFile("Invalid.yml")
	if err == nil {
		t.Error("Expected error")
	}
}

func TestCheck(t *testing.T) {
	SetConfigFile("conf/dbshield.yml")
	err := Check()
	if err != nil {
		t.Error("Got error", err)
	}
}

func TestPostConfig(t *testing.T) {
	SetConfigFile("conf/dbshield.yml")
	config.Config.DBType = "Invalid"
	err := postConfig()
	if err == nil {
		t.Error("Expected error")
	}

	config.Config.ListenPort = 0
	config.Config.DBType = "mysql"
	err = postConfig()
	if err != nil {
		t.Error("Expected nil got ", err)
	}

	config.Config.TLSCertificate = os.TempDir()
	err = postConfig()
	if err == nil {
		t.Error("Expected error")
	}
}

func TestEveryThing(t *testing.T) {
	closeHandlers()
	SetConfigFile("conf/dbshield.yml")
	//It should fail if port is already open
	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.Config.ListenIP, config.Config.ListenPort))
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()

	err = mainListner()
	if err == nil {
		t.Error("Expected error")
	}

	go func() {
		timer := time.NewTimer(time.Second * 2)
		<-timer.C
		syscall.Kill(syscall.Getpid(), syscall.SIGINT)
	}()
	err = Start()
	if err != nil {
		t.Error("Got error", err)
	}
	file, _ := ioutil.TempFile(os.TempDir(), "tempDB")
	defer os.Remove(file.Name())
	training.DBCon, _ = bolt.Open(file.Name(), 0600, nil)
	training.DBCon.Update(func(tx *bolt.Tx) error {
		tx.CreateBucket([]byte("pattern"))
		tx.CreateBucket([]byte("abnormal"))
		tx.CreateBucket([]byte("state"))
		return nil
	})
	c := sql.QueryContext{
		Query:    []byte("select * from test;"),
		Database: []byte("test"),
		User:     []byte("test"),
		Client:   []byte("127.0.0.1"),
		Time:     time.Now(),
	}
	training.CheckQuery(c)
	err = training.AddToTrainingSet(c)
	if err != nil {
		t.Error("Got error", err)
	}

	Patterns()
	Abnormals()

	//Test without bucket
	tmpCon := training.DBCon
	defer func() {
		training.DBCon = tmpCon
	}()
	tmpfile, err := ioutil.TempFile("", "testdb")
	if err != nil {
		panic(err)
	}
	defer tmpfile.Close()
	path := tmpfile.Name()
	training.DBCon, err = bolt.Open(path, 0600, nil)
	if err != nil {
		panic(err)
	}
	Patterns()
	Abnormals()
}
<|endoftext|>