<commit_before>package gotabulate

import "fmt"
import "bytes"
import "github.com/mattn/go-runewidth"
import "math"

// Basic Structure of TableFormat
type TableFormat struct {
	LineTop         Line
	LineBelowHeader Line
	LineBetweenRows Line
	LineBottom      Line
	HeaderRow       Row
	DataRow         Row
	Padding         int
	HeaderHide      bool
	FitScreen       bool
}

// Represents a Line
type Line struct {
	begin string
	hline string
	sep   string
	end   string
}

// Represents a Row
type Row struct {
	begin string
	sep   string
	end   string
}

// Table Formats that are available to the user
// The user can define his own format, just by addind an entry to this map
// and calling it with Render function e.g t.Render("customFormat")
var TableFormats = map[string]TableFormat{
	"simple": TableFormat{
		LineTop:         Line{"", "-", "  ", ""},
		LineBelowHeader: Line{"", "-", "  ", ""},
		LineBottom:      Line{"", "-", "  ", ""},
		HeaderRow:       Row{"", "  ", ""},
		DataRow:         Row{"", "  ", ""},
		Padding:         1,
	},
	"plain": TableFormat{
		HeaderRow: Row{"", "  ", ""},
		DataRow:   Row{"", "  ", ""},
		Padding:   1,
	},
	"grid": TableFormat{
		LineTop:         Line{"+", "-", "+", "+"},
		LineBelowHeader: Line{"+", "=", "+", "+"},
		LineBetweenRows: Line{"+", "-", "+", "+"},
		LineBottom:      Line{"+", "-", "+", "+"},
		HeaderRow:       Row{"|", "|", "|"},
		DataRow:         Row{"|", "|", "|"},
		Padding:         1,
	},
}

// Minimum padding that will be applied
var MIN_PADDING = 5

// Main Tabulate structure
type Tabulate struct {
	Data        []*TabulateRow
	Headers     []string
	FloatFormat byte
	TableFormat TableFormat
	Align       string
	EmptyVar    string
	HideLines   []string
	MaxSize     int
	WrapStrings bool
}

// Represents normalized tabulate Row
type TabulateRow struct {
	Elements  []string
	Continuos bool
}

type writeBuffer struct {
	Buffer bytes.Buffer
}

func createBuffer() *writeBuffer {
	return &writeBuffer{}
}

func (b *writeBuffer) Write(str string, count int) *writeBuffer {
	for i := 0; i < count; i++ {
		b.Buffer.WriteString(str)
	}
	return b
}
func (b *writeBuffer) String() string {
	return b.Buffer.String()
}

// Add padding to each cell
func (t *Tabulate) padRow(arr []string, padding int) []string {
	if len(arr) < 1 {
		return arr
	}
	padded := make([]string, len(arr))
	for index, el := range arr {
		b := createBuffer()
		b.Write(" ", padding)
		b.Write(el, 1)
		b.Write(" ", padding)
		padded[index] = b.String()
	}
	return padded
}

// Align right (Add padding left)
func (t *Tabulate) padLeft(width int, str string) string {
	b := createBuffer()
	b.Write(" ", (width - runewidth.StringWidth(str)))
	b.Write(str, 1)
	return b.String()
}

// Align Left (Add padding right)
func (t *Tabulate) padRight(width int, str string) string {
	b := createBuffer()
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(str)))
	return b.String()
}

// Center the element in the cell
func (t *Tabulate) padCenter(width int, str string) string {
	b := createBuffer()
	padding := int(math.Ceil(float64((width - runewidth.StringWidth(str))) / 2.0))
	b.Write(" ", padding)
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(b.String())))

	return b.String()
}

// Build Line based on padded_widths from t.GetWidths()
func (t *Tabulate) buildLine(padded_widths []int, padding []int, l Line) string {
	cells := make([]string, len(padded_widths))

	for i, _ := range cells {
		b := createBuffer()
		b.Write(l.hline, padding[i]+MIN_PADDING)
		cells[i] = b.String()
	}

	var buffer bytes.Buffer
	buffer.WriteString(l.begin)

	// Print contents
	for i := 0; i < len(cells); i++ {
		buffer.WriteString(cells[i])
		if i != len(cells)-1 {
			buffer.WriteString(l.sep)
		}
	}

	buffer.WriteString(l.end)
	return buffer.String()
}

// Build Row based on padded_widths from t.GetWidths()
func (t *Tabulate) buildRow(elements []string, padded_widths []int, paddings []int, d Row) string {

	var buffer bytes.Buffer
	buffer.WriteString(d.begin)
	padFunc := t.getAlignFunc()
	// Print contents
	for i := 0; i < len(padded_widths); i++ {
		output := ""
		if len(elements) <= i || (len(elements) > i && elements[i] == " nil ") {
			output = padFunc(padded_widths[i], t.EmptyVar)
		} else if len(elements) > i {
			output = padFunc(padded_widths[i], elements[i])
		}
		buffer.WriteString(output)
		if i != len(padded_widths)-1 {
			buffer.WriteString(d.sep)
		}
	}

	buffer.WriteString(d.end)
	return buffer.String()
}

// Render the data table
func (t *Tabulate) Render(format ...interface{}) string {
	var lines []string

	// If headers are set use them, otherwise pop the first row
	if len(t.Headers) < 1 {
		t.Headers, t.Data = t.Data[0].Elements, t.Data[1:]
	}

	// Use the format that was passed as parameter, otherwise
	// use the format defined in the struct
	if len(format) > 0 {
		t.TableFormat = TableFormats[format[0].(string)]
	}

	// If Wrap Strings is set to True,then break up the string to multiple cells
	if t.WrapStrings {
		t.Data = t.wrapCellData()
	}

	// Check if Data is present
	if len(t.Data) < 1 {
		panic("No Data specified")
	}

	if len(t.Headers) < len(t.Data[0].Elements) {
		diff := len(t.Data[0].Elements) - len(t.Headers)
		padded_header := make([]string, diff)
		for _, e := range t.Headers {
			padded_header = append(padded_header, e)
		}
		t.Headers = padded_header
	}

	// Get Column widths for all columns
	cols := t.getWidths(t.Headers, t.Data)

	padded_widths := make([]int, len(cols))
	for i, _ := range padded_widths {
		padded_widths[i] = cols[i] + MIN_PADDING*t.TableFormat.Padding
	}

	// Start appending lines

	// Append top line if not hidden
	if !inSlice("top", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineTop))
	}

	// Add Header
	lines = append(lines, t.buildRow(t.padRow(t.Headers, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.HeaderRow))

	// Add Line Below Header if not hidden
	if !inSlice("belowheader", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBelowHeader))
	}

	// Add Data Rows
	for index, element := range t.Data {
		lines = append(lines, t.buildRow(t.padRow(element.Elements, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.DataRow))
		if index < len(t.Data)-1 {
			if element.Continuos != true {
				lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBetweenRows))
			}
		}
	}

	if !inSlice("bottomLine", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBottom))
	}

	// Join lines
	var buffer bytes.Buffer
	for _, line := range lines {
		buffer.WriteString(line + "\n")
	}

	return buffer.String()
}

// Calculate the max column width for each element
func (t *Tabulate) getWidths(headers []string, data []*TabulateRow) []int {
	widths := make([]int, len(headers))
	current_max := len(t.EmptyVar)
	for i := 0; i < len(headers); i++ {
		current_max = runewidth.StringWidth(headers[i])
		for _, item := range data {
			if len(item.Elements) > i && len(widths) > i {
				element := item.Elements[i]
				strLength := runewidth.StringWidth(element)
				if strLength > current_max {
					widths[i] = strLength
					current_max = strLength
				} else {
					widths[i] = current_max
				}
			}
		}
	}

	return widths
}

// Set Headers of the table
// If Headers count is less than the data row count, the headers will be padded to the right
func (t *Tabulate) SetHeaders(headers []string) *Tabulate {
	t.Headers = headers
	return t
}

// Set Float Formatting
// will be used in strconv.FormatFloat(element, format, -1, 64)
func (t *Tabulate) SetFloatFormat(format byte) *Tabulate {
	t.FloatFormat = format
	return t
}

// Set Align Type, Available options: left, right, center
func (t *Tabulate) SetAlign(align string) {
	t.Align = align
}

// Select the padding function based on the align type
func (t *Tabulate) getAlignFunc() func(int, string) string {
	if len(t.Align) < 1 || t.Align == "right" {
		return t.padLeft
	} else if t.Align == "left" {
		return t.padRight
	} else {
		return t.padCenter
	}
}

// Set how an empty cell will be represented
func (t *Tabulate) SetEmptyString(empty string) {
	t.EmptyVar = empty + " "
}

// Set which lines to hide.
// Can be:
// top - Top line of the table,
// belowheader - Line below the header,
// bottom - Bottom line of the table
func (t *Tabulate) SetHideLines(hide []string) {
	t.HideLines = hide
}

func (t *Tabulate) SetWrapStrings(wrap bool) {
	t.WrapStrings = wrap
}

// Sets the maximum size of cell
// If WrapStrings is set to true, then the string inside
// the cell will be split up into multiple cell
func (t *Tabulate) SetMaxCellSize(max int) {
	t.MaxSize = max
}

// If string size is larger than t.MaxSize, then split it to multiple cells (downwards)
func (t *Tabulate) wrapCellData() []*TabulateRow {
	var arr []*TabulateRow
	next := t.Data[0]
	for index := 0; index <= len(t.Data); index++ {
		elements := next.Elements
		new_elements := make([]string, len(elements))

		for i, e := range elements {
			if runewidth.StringWidth(e) > t.MaxSize {
				elements[i] = runewidth.Truncate(e, t.MaxSize, "")
				new_elements[i] = e[len(elements[i]):]
				next.Continuos = true
			}
		}

		if next.Continuos {
			arr = append(arr, next)
			next = &TabulateRow{Elements: new_elements}
			index--
		} else if index+1 < len(t.Data) {
			arr = append(arr, next)
			next = t.Data[index+1]
		} else if index >= len(t.Data) {
			arr = append(arr, next)
		}

	}
	return arr
}

// Create a new Tabulate Object
// Accepts 2D String Array, 2D Int Array, 2D Int64 Array,
// 2D Bool Array, 2D Float64 Array, 2D interface{} Array,
// Map map[strig]string, Map map[string]interface{},
func Create(data interface{}) *Tabulate {
	t := &Tabulate{FloatFormat: 'f', MaxSize: 30}

	switch v := data.(type) {
	case [][]string:
		t.Data = createFromString(data.([][]string))
	case [][]int32:
		t.Data = createFromInt32(data.([][]int32))
	case [][]int64:
		t.Data = createFromInt64(data.([][]int64))
	case [][]int:
		t.Data = createFromInt(data.([][]int))
	case [][]bool:
		t.Data = createFromBool(data.([][]bool))
	case [][]float64:
		t.Data = createFromFloat64(data.([][]float64), t.FloatFormat)
	case [][]interface{}:
		t.Data = createFromMixed(data.([][]interface{}), t.FloatFormat)
	case []string:
		t.Data = createFromString([][]string{data.([]string)})
	case []interface{}:
		t.Data = createFromMixed([][]interface{}{data.([]interface{})}, t.FloatFormat)
	case map[string][]interface{}:
		t.Headers, t.Data = createFromMapMixed(data.(map[string][]interface{}), t.FloatFormat)
	case map[string][]string:
		t.Headers, t.Data = createFromMapString(data.(map[string][]string))
	default:
		fmt.Println(v)
	}

	return t
}
<commit_msg>Building out intelligent line wrapping<commit_after>package gotabulate

import "fmt"
import "bytes"
import "github.com/mattn/go-runewidth"
import "unicode/utf8"
import "math"

// Basic Structure of TableFormat
type TableFormat struct {
	LineTop         Line
	LineBelowHeader Line
	LineBetweenRows Line
	LineBottom      Line
	HeaderRow       Row
	DataRow         Row
	Padding         int
	HeaderHide      bool
	FitScreen       bool
}

// Represents a Line
type Line struct {
	begin string
	hline string
	sep   string
	end   string
}

// Represents a Row
type Row struct {
	begin string
	sep   string
	end   string
}

// Table Formats that are available to the user
// The user can define his own format, just by addind an entry to this map
// and calling it with Render function e.g t.Render("customFormat")
var TableFormats = map[string]TableFormat{
	"simple": TableFormat{
		LineTop:         Line{"", "-", "  ", ""},
		LineBelowHeader: Line{"", "-", "  ", ""},
		LineBottom:      Line{"", "-", "  ", ""},
		HeaderRow:       Row{"", "  ", ""},
		DataRow:         Row{"", "  ", ""},
		Padding:         1,
	},
	"plain": TableFormat{
		HeaderRow: Row{"", "  ", ""},
		DataRow:   Row{"", "  ", ""},
		Padding:   1,
	},
	"grid": TableFormat{
		LineTop:         Line{"+", "-", "+", "+"},
		LineBelowHeader: Line{"+", "=", "+", "+"},
		LineBetweenRows: Line{"+", "-", "+", "+"},
		LineBottom:      Line{"+", "-", "+", "+"},
		HeaderRow:       Row{"|", "|", "|"},
		DataRow:         Row{"|", "|", "|"},
		Padding:         1,
	},
}

// Minimum padding that will be applied
var MIN_PADDING = 5

// Main Tabulate structure
type Tabulate struct {
	Data          []*TabulateRow
	Headers       []string
	FloatFormat   byte
	TableFormat   TableFormat
	Align         string
	EmptyVar      string
	HideLines     []string
	MaxSize       int
	WrapStrings   bool
	WrapDelimiter rune
	SplitConcat   string
}

// Represents normalized tabulate Row
type TabulateRow struct {
	Elements  []string
	Continuos bool
}

type writeBuffer struct {
	Buffer bytes.Buffer
}

func createBuffer() *writeBuffer {
	return &writeBuffer{}
}

func (b *writeBuffer) Write(str string, count int) *writeBuffer {
	for i := 0; i < count; i++ {
		b.Buffer.WriteString(str)
	}
	return b
}
func (b *writeBuffer) String() string {
	return b.Buffer.String()
}

// Add padding to each cell
func (t *Tabulate) padRow(arr []string, padding int) []string {
	if len(arr) < 1 {
		return arr
	}
	padded := make([]string, len(arr))
	for index, el := range arr {
		b := createBuffer()
		b.Write(" ", padding)
		b.Write(el, 1)
		b.Write(" ", padding)
		padded[index] = b.String()
	}
	return padded
}

// Align right (Add padding left)
func (t *Tabulate) padLeft(width int, str string) string {
	b := createBuffer()
	b.Write(" ", (width - runewidth.StringWidth(str)))
	b.Write(str, 1)
	return b.String()
}

// Align Left (Add padding right)
func (t *Tabulate) padRight(width int, str string) string {
	b := createBuffer()
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(str)))
	return b.String()
}

// Center the element in the cell
func (t *Tabulate) padCenter(width int, str string) string {
	b := createBuffer()
	padding := int(math.Ceil(float64((width - runewidth.StringWidth(str))) / 2.0))
	b.Write(" ", padding)
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(b.String())))

	return b.String()
}

// Build Line based on padded_widths from t.GetWidths()
func (t *Tabulate) buildLine(padded_widths []int, padding []int, l Line) string {
	cells := make([]string, len(padded_widths))

	for i, _ := range cells {
		b := createBuffer()
		b.Write(l.hline, padding[i]+MIN_PADDING)
		cells[i] = b.String()
	}

	var buffer bytes.Buffer
	buffer.WriteString(l.begin)

	// Print contents
	for i := 0; i < len(cells); i++ {
		buffer.WriteString(cells[i])
		if i != len(cells)-1 {
			buffer.WriteString(l.sep)
		}
	}

	buffer.WriteString(l.end)
	return buffer.String()
}

// Build Row based on padded_widths from t.GetWidths()
func (t *Tabulate) buildRow(elements []string, padded_widths []int, paddings []int, d Row) string {

	var buffer bytes.Buffer
	buffer.WriteString(d.begin)
	padFunc := t.getAlignFunc()
	// Print contents
	for i := 0; i < len(padded_widths); i++ {
		output := ""
		if len(elements) <= i || (len(elements) > i && elements[i] == " nil ") {
			output = padFunc(padded_widths[i], t.EmptyVar)
		} else if len(elements) > i {
			output = padFunc(padded_widths[i], elements[i])
		}
		buffer.WriteString(output)
		if i != len(padded_widths)-1 {
			buffer.WriteString(d.sep)
		}
	}

	buffer.WriteString(d.end)
	return buffer.String()
}

//SetWrapDelimiter assigns the character ina  string that the rednderer
//will attempt to split strings on when a cell must be wrapped
func (t *Tabulate) SetWrapDelimiter(r rune) {
	t.WrapDelimiter = r
}

//SetSplitConcat assigns the character that will be used when a WrapDelimiter is
//set but the renderer cannot abide by the desired split.  This may happen when
//the WrapDelimiter is a space ' ' but a single word is longer than the width of a cell
func (t *Tabulate) SetSplitConcat(r string) {
	t.SplitConcat = r
}

// Render the data table
func (t *Tabulate) Render(format ...interface{}) string {
	var lines []string

	// If headers are set use them, otherwise pop the first row
	if len(t.Headers) < 1 {
		t.Headers, t.Data = t.Data[0].Elements, t.Data[1:]
	}

	// Use the format that was passed as parameter, otherwise
	// use the format defined in the struct
	if len(format) > 0 {
		t.TableFormat = TableFormats[format[0].(string)]
	}

	// If Wrap Strings is set to True,then break up the string to multiple cells
	if t.WrapStrings {
		t.Data = t.wrapCellData()
	}

	// Check if Data is present
	if len(t.Data) < 1 {
		panic("No Data specified")
	}

	if len(t.Headers) < len(t.Data[0].Elements) {
		diff := len(t.Data[0].Elements) - len(t.Headers)
		padded_header := make([]string, diff)
		for _, e := range t.Headers {
			padded_header = append(padded_header, e)
		}
		t.Headers = padded_header
	}

	// Get Column widths for all columns
	cols := t.getWidths(t.Headers, t.Data)

	padded_widths := make([]int, len(cols))
	for i, _ := range padded_widths {
		padded_widths[i] = cols[i] + MIN_PADDING*t.TableFormat.Padding
	}

	// Start appending lines

	// Append top line if not hidden
	if !inSlice("top", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineTop))
	}

	// Add Header
	lines = append(lines, t.buildRow(t.padRow(t.Headers, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.HeaderRow))

	// Add Line Below Header if not hidden
	if !inSlice("belowheader", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBelowHeader))
	}

	// Add Data Rows
	for index, element := range t.Data {
		lines = append(lines, t.buildRow(t.padRow(element.Elements, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.DataRow))
		if index < len(t.Data)-1 {
			if element.Continuos != true {
				lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBetweenRows))
			}
		}
	}

	if !inSlice("bottomLine", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBottom))
	}

	// Join lines
	var buffer bytes.Buffer
	for _, line := range lines {
		buffer.WriteString(line + "\n")
	}

	return buffer.String()
}

// Calculate the max column width for each element
func (t *Tabulate) getWidths(headers []string, data []*TabulateRow) []int {
	widths := make([]int, len(headers))
	current_max := len(t.EmptyVar)
	for i := 0; i < len(headers); i++ {
		current_max = runewidth.StringWidth(headers[i])
		for _, item := range data {
			if len(item.Elements) > i && len(widths) > i {
				element := item.Elements[i]
				strLength := runewidth.StringWidth(element)
				if strLength > current_max {
					widths[i] = strLength
					current_max = strLength
				} else {
					widths[i] = current_max
				}
			}
		}
	}

	return widths
}

// Set Headers of the table
// If Headers count is less than the data row count, the headers will be padded to the right
func (t *Tabulate) SetHeaders(headers []string) *Tabulate {
	t.Headers = headers
	return t
}

// Set Float Formatting
// will be used in strconv.FormatFloat(element, format, -1, 64)
func (t *Tabulate) SetFloatFormat(format byte) *Tabulate {
	t.FloatFormat = format
	return t
}

// Set Align Type, Available options: left, right, center
func (t *Tabulate) SetAlign(align string) {
	t.Align = align
}

// Select the padding function based on the align type
func (t *Tabulate) getAlignFunc() func(int, string) string {
	if len(t.Align) < 1 || t.Align == "right" {
		return t.padLeft
	} else if t.Align == "left" {
		return t.padRight
	} else {
		return t.padCenter
	}
}

// Set how an empty cell will be represented
func (t *Tabulate) SetEmptyString(empty string) {
	t.EmptyVar = empty + " "
}

// Set which lines to hide.
// Can be:
// top - Top line of the table,
// belowheader - Line below the header,
// bottom - Bottom line of the table
func (t *Tabulate) SetHideLines(hide []string) {
	t.HideLines = hide
}

func (t *Tabulate) SetWrapStrings(wrap bool) {
	t.WrapStrings = wrap
}

// Sets the maximum size of cell
// If WrapStrings is set to true, then the string inside
// the cell will be split up into multiple cell
func (t *Tabulate) SetMaxCellSize(max int) {
	t.MaxSize = max
}

func (t *Tabulate) splitElement(e string) (bool, string) {
	//check if we are not attempting to smartly wrap
	if t.WrapDelimiter == 0 {
		if t.SplitConcat == "" {
			return false, runewidth.Truncate(e, t.MaxSize, "")
		} else {
			return false, runewidth.Truncate(e, t.MaxSize, t.SplitConcat)
		}
	}

	//we are attempting to wrap
	//grab the current width
	var i int
	for i = t.MaxSize; i > 1; i-- {
		//loop through our proposed truncation size looking for one that ends on
		//our requested delimiter
		x := runewidth.Truncate(e, i, "")
		//check if the NEXT string is a
		//delimiter, if it IS, then we truncate and tell the caller to shrink
		r, _ := utf8.DecodeRuneInString(e[i:])
		if r == 0 || r == 1 {
			//decode failed, take the truncation as is
			return false, x
		}
		if r == t.WrapDelimiter {
			return true, x //inform the caller that they can remove the next rune
		}
	}
	//didn't find a good length, truncate at will
	if t.SplitConcat != "" {
		return false, runewidth.Truncate(e, t.MaxSize, t.SplitConcat)
	}
	return false, runewidth.Truncate(e, t.MaxSize, "")
}

// If string size is larger than t.MaxSize, then split it to multiple cells (downwards)
func (t *Tabulate) wrapCellData() []*TabulateRow {
	var arr []*TabulateRow
	var cleanSplit bool
	var addr int
	next := t.Data[0]
	for index := 0; index <= len(t.Data); index++ {
		elements := next.Elements
		new_elements := make([]string, len(elements))

		for i, e := range elements {
			if runewidth.StringWidth(e) > t.MaxSize {
				cleanSplit, elements[i] = t.splitElement(e)
				if cleanSplit {
					//remove the next rune
					r, w := utf8.DecodeRuneInString(e[len(elements[i]):])
					if r != 0 && r != 1 {
						addr = w
					}
				} else {
					addr = 0
				}
				new_elements[i] = e[len(elements[i+addr]):]
				next.Continuos = true
			}
		}

		if next.Continuos {
			arr = append(arr, next)
			next = &TabulateRow{Elements: new_elements}
			index--
		} else if index+1 < len(t.Data) {
			arr = append(arr, next)
			next = t.Data[index+1]
		} else if index >= len(t.Data) {
			arr = append(arr, next)
		}

	}
	return arr
}

// Create a new Tabulate Object
// Accepts 2D String Array, 2D Int Array, 2D Int64 Array,
// 2D Bool Array, 2D Float64 Array, 2D interface{} Array,
// Map map[strig]string, Map map[string]interface{},
func Create(data interface{}) *Tabulate {
	t := &Tabulate{FloatFormat: 'f', MaxSize: 30}

	switch v := data.(type) {
	case [][]string:
		t.Data = createFromString(data.([][]string))
	case [][]int32:
		t.Data = createFromInt32(data.([][]int32))
	case [][]int64:
		t.Data = createFromInt64(data.([][]int64))
	case [][]int:
		t.Data = createFromInt(data.([][]int))
	case [][]bool:
		t.Data = createFromBool(data.([][]bool))
	case [][]float64:
		t.Data = createFromFloat64(data.([][]float64), t.FloatFormat)
	case [][]interface{}:
		t.Data = createFromMixed(data.([][]interface{}), t.FloatFormat)
	case []string:
		t.Data = createFromString([][]string{data.([]string)})
	case []interface{}:
		t.Data = createFromMixed([][]interface{}{data.([]interface{})}, t.FloatFormat)
	case map[string][]interface{}:
		t.Headers, t.Data = createFromMapMixed(data.(map[string][]interface{}), t.FloatFormat)
	case map[string][]string:
		t.Headers, t.Data = createFromMapString(data.(map[string][]string))
	default:
		fmt.Println(v)
	}

	return t
}
<|endoftext|>


<commit_before>package controllers

import (
	r "github.com/revel/revel"
	"github.com/richtr/baseapp/app/models"
	"github.com/richtr/baseapp/app/routes"
	"net/url"
	"strings"
)

type Application struct {
	*r.Controller
	Account
}

func (c Application) Index() r.Result {
	return c.Render()
}

func (c Application) About() r.Result {
	return c.Render()
}

func (c Application) Contact() r.Result {
	return c.Render()
}

func (c Application) Search(query string, page int) r.Result {

	var matchedProfiles []*models.Profile

	if page == 0 {
		page = 1
	}
	nextPage := page + 1
	size := 50; // results per page

	if(query != "") {

		// Format query value
		sql_query_value, _ := url.QueryUnescape(query);
		sql_query_value = strings.Trim(sql_query_value, " @#")
		sql_query_value = strings.ToLower(sql_query_value)

		sql_query_value_full := "%" + sql_query_value + "%"
		sql_query_value_front := sql_query_value + "%"
		sql_query_value_back := "%" + sql_query_value

		sql_query_string := "SELECT * FROM Profile WHERE username LIKE ? OR name LIKE ? ORDER BY CASE WHEN username LIKE ? THEN 1 WHEN name LIKE ? THEN 2 WHEN username LIKE ? THEN 4 WHEN name LIKE ? THEN 5 ELSE 3 END LIMIT ?, ?"

		// Retrieve all profiles loosely matching search term
		results, err := c.Txn.Select(models.Profile{}, sql_query_string, sql_query_value_full, sql_query_value_full, sql_query_value_front, sql_query_value_front, sql_query_value_back, sql_query_value_back, (page-1)*size, size)

		if err == nil {
			for _, r := range results {
				matchedProfiles = append(matchedProfiles, r.(*models.Profile))
			}
		}

		if len(matchedProfiles) == 0 && page != 1 {
			return c.Redirect(routes.Application.Search(query, 1))
		}

	}

	return c.Render(query, matchedProfiles, page, nextPage)
}

func (c Application) SwitchToDesktop() r.Result {
	// Add desktop mode cookie
	c.Session["desktopmode"] = "1"

	// TODO: redirect back to referrer
	return c.Redirect(routes.Application.Index())
}

func (c Application) SwitchToMobile() r.Result {
	// Remove desktop mode cookie
	delete(c.Session, "desktopmode")

	// TODO: redirect back to referrer
	return c.Redirect(routes.Application.Index())
}
<commit_msg>Redirect user back to current page when desktopmode session cookie has been set<commit_after>package controllers

import (
	r "github.com/revel/revel"
	"github.com/richtr/baseapp/app/models"
	"github.com/richtr/baseapp/app/routes"
	"net/url"
	"strings"
)

type Application struct {
	*r.Controller
	Account
}

func (c Application) Index() r.Result {
	return c.Render()
}

func (c Application) About() r.Result {
	return c.Render()
}

func (c Application) Contact() r.Result {
	return c.Render()
}

func (c Application) Search(query string, page int) r.Result {

	var matchedProfiles []*models.Profile

	if page == 0 {
		page = 1
	}
	nextPage := page + 1
	size := 50; // results per page

	if(query != "") {

		// Format query value
		sql_query_value, _ := url.QueryUnescape(query);
		sql_query_value = strings.Trim(sql_query_value, " @#")
		sql_query_value = strings.ToLower(sql_query_value)

		sql_query_value_full := "%" + sql_query_value + "%"
		sql_query_value_front := sql_query_value + "%"
		sql_query_value_back := "%" + sql_query_value

		sql_query_string := "SELECT * FROM Profile WHERE username LIKE ? OR name LIKE ? ORDER BY CASE WHEN username LIKE ? THEN 1 WHEN name LIKE ? THEN 2 WHEN username LIKE ? THEN 4 WHEN name LIKE ? THEN 5 ELSE 3 END LIMIT ?, ?"

		// Retrieve all profiles loosely matching search term
		results, err := c.Txn.Select(models.Profile{}, sql_query_string, sql_query_value_full, sql_query_value_full, sql_query_value_front, sql_query_value_front, sql_query_value_back, sql_query_value_back, (page-1)*size, size)

		if err == nil {
			for _, r := range results {
				matchedProfiles = append(matchedProfiles, r.(*models.Profile))
			}
		}

		if len(matchedProfiles) == 0 && page != 1 {
			return c.Redirect(routes.Application.Search(query, 1))
		}

	}

	return c.Render(query, matchedProfiles, page, nextPage)
}

func (c Application) SwitchToDesktop() r.Result {
	// Add desktop mode cookie
	c.Session["desktopmode"] = "1"

	referer, err := url.Parse( c.Request.Request.Header.Get("Referer") )
	if err != nil || referer.String() == "" {
		return c.Redirect(routes.Application.Index())
	}

	return c.Redirect(referer.String())
}

func (c Application) SwitchToMobile() r.Result {
	// Remove desktop mode cookie
	delete(c.Session, "desktopmode")

	referer, err := url.Parse( c.Request.Request.Header.Get("Referer") )
	if err != nil || referer.String() == "" {
		return c.Redirect(routes.Application.Index())
	}

	return c.Redirect(referer.String())
}
<|endoftext|>


<commit_before>// Package files is for storing files on the cozy, including binary ones like
// photos and movies. The range of possible operations with this endpoint goes
// from simple ones, like uploading a file, to more complex ones, like renaming
// a folder. It also ensure that an instance is not exceeding its quota, and
// keeps a trash to recover files recently deleted.
package files

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strings"

	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/gin-gonic/gin"
	"github.com/spf13/afero"
)

// DefaultContentType is used for files uploaded with no content-type
const DefaultContentType = "application/octet-stream"

// DocType is the type of document, eg. file or folder
type DocType string

const (
	// FileDocType is document type
	FileDocType DocType = "io.cozy.files"
	// FolderDocType is document type
	FolderDocType = "io.cozy.folders"
)

var (
	errDocAlreadyExists = errors.New("Directory already exists")
	errDocTypeInvalid   = errors.New("Invalid document type")
	errIllegalFilename  = errors.New("Invalid filename: empty or contains one of these illegal characters: / \\ : ? * \" |")
)

var regFileName = regexp.MustCompile("[\\/\\\\:\\?\\*\"|]+")

// DocMetadata encapsulates the few metadata linked to a document
// creation request.
type DocMetadata struct {
	Type       DocType
	Name       string
	FolderID   string
	Executable bool
	Tags       []string
}

func (metadata *DocMetadata) path() string {
	return metadata.FolderID + "/" + metadata.Name
}

// NewDocMetadata is the DocMetadata constructor. All inputs are
// validated and if wrong, an error is returned.
func NewDocMetadata(docTypeStr, name, folderID, tagsStr string, executable bool) (*DocMetadata, error) {
	docType, err := parseDocType(docTypeStr)
	if err != nil {
		return nil, err
	}

	if err = checkFileName(name); err != nil {
		return nil, err
	}

	// FolderID is not mandatory. If empty, the document is at the root
	// of the FS
	if folderID != "" {
		if err = checkFileName(folderID); err != nil {
			return nil, err
		}
	}

	tags := parseTags(tagsStr)

	return &DocMetadata{
		Type:       docType,
		Name:       name,
		FolderID:   folderID,
		Tags:       tags,
		Executable: executable,
	}, nil
}

// Upload is the method for uploading a file
//
// This will be used to upload a file
// @TODO
func Upload(metadata *DocMetadata, storage afero.Fs, body io.ReadCloser) error {
	if metadata.Type != FileDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	defer body.Close()
	return afero.SafeWriteReader(storage, path, body)
}

// CreateDirectory is the method for creating a new directory
//
// @TODO
func CreateDirectory(metadata *DocMetadata, storage afero.Fs) error {
	if metadata.Type != FolderDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	exists, err := afero.DirExists(storage, path)
	if err != nil {
		return err
	}
	if exists {
		return errDocAlreadyExists
	}

	return storage.Mkdir(path, 0777)
}

// CreationHandler handle all POST requests on /files/:folder-id
// aiming at creating a new document in the FS. Given the Type
// parameter of the request, it will either upload a new file or
// create a new directory.
//
// swagger:route POST /files/:folder-id files uploadFileOrCreateDir
func CreationHandler(c *gin.Context) {
	instance, err := middlewares.GetInstance(c)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	storage, err := instance.GetStorageProvider()
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	metadata, err := NewDocMetadata(
		c.Query("Type"),
		c.Query("Name"),
		c.Param("folder-id"),
		c.Query("Tags"),
		c.Query("Executable") == "true",
	)

	if err != nil {
		c.AbortWithError(http.StatusUnprocessableEntity, err)
		return
	}

	contentType := c.ContentType()
	if contentType == "" {
		contentType = DefaultContentType
	}

	exists, err := checkParentFolderID(storage, metadata.FolderID)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	if !exists {
		err = fmt.Errorf("Parent folder with given FolderID does not exist")
		c.AbortWithError(http.StatusNotFound, err)
		return
	}

	fmt.Printf("%s:\n\t- %+v\n\t- %v\n", metadata.Name, metadata, contentType)

	switch metadata.Type {
	case FileDocType:
		err = Upload(metadata, storage, c.Request.Body)
	case FolderDocType:
		err = CreateDirectory(metadata, storage)
	}

	if err != nil {
		var code int
		switch err {
		case errDocAlreadyExists:
			code = http.StatusConflict
		default:
			code = http.StatusInternalServerError
		}
		c.AbortWithError(code, err)
		return
	}

	data := []byte{'O', 'K'}
	c.Data(http.StatusCreated, jsonapi.ContentType, data)
}

// Routes sets the routing for the files service
func Routes(router *gin.RouterGroup) {
	router.POST("/:folder-id", CreationHandler)
}

func parseTags(str string) []string {
	var tags []string
	for _, tag := range strings.Split(str, ",") {
		// @TODO: more sanitization maybe ?
		tag = strings.TrimSpace(tag)
		if tag != "" {
			tags = append(tags, tag)
		}
	}
	return tags
}

func parseDocType(docType string) (DocType, error) {
	var result DocType
	var err error
	switch docType {
	case "io.cozy.files":
		result = FileDocType
	case "io.cozy.folders":
		result = FolderDocType
	default:
		err = errDocTypeInvalid
	}
	return result, err
}

func checkFileName(str string) error {
	if str == "" || regFileName.MatchString(str) {
		return errIllegalFilename
	}
	return nil
}

func checkParentFolderID(storage afero.Fs, folderID string) (bool, error) {
	if folderID == "" {
		return true, nil
	}

	exists, err := afero.DirExists(storage, folderID)
	if err != nil {
		return false, err
	}
	if !exists {
		return false, nil
	}

	return true, nil
}
<commit_msg>Enable POST /files/ route<commit_after>// Package files is for storing files on the cozy, including binary ones like
// photos and movies. The range of possible operations with this endpoint goes
// from simple ones, like uploading a file, to more complex ones, like renaming
// a folder. It also ensure that an instance is not exceeding its quota, and
// keeps a trash to recover files recently deleted.
package files

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strings"

	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/gin-gonic/gin"
	"github.com/spf13/afero"
)

// DefaultContentType is used for files uploaded with no content-type
const DefaultContentType = "application/octet-stream"

// DocType is the type of document, eg. file or folder
type DocType string

const (
	// FileDocType is document type
	FileDocType DocType = "io.cozy.files"
	// FolderDocType is document type
	FolderDocType = "io.cozy.folders"
)

var (
	errDocAlreadyExists = errors.New("Directory already exists")
	errDocTypeInvalid   = errors.New("Invalid document type")
	errIllegalFilename  = errors.New("Invalid filename: empty or contains one of these illegal characters: / \\ : ? * \" |")
)

var regFileName = regexp.MustCompile("[\\/\\\\:\\?\\*\"|]+")

// DocMetadata encapsulates the few metadata linked to a document
// creation request.
type DocMetadata struct {
	Type       DocType
	Name       string
	FolderID   string
	Executable bool
	Tags       []string
}

func (metadata *DocMetadata) path() string {
	return metadata.FolderID + "/" + metadata.Name
}

// NewDocMetadata is the DocMetadata constructor. All inputs are
// validated and if wrong, an error is returned.
func NewDocMetadata(docTypeStr, name, folderID, tagsStr string, executable bool) (*DocMetadata, error) {
	docType, err := parseDocType(docTypeStr)
	if err != nil {
		return nil, err
	}

	if err = checkFileName(name); err != nil {
		return nil, err
	}

	// FolderID is not mandatory. If empty, the document is at the root
	// of the FS
	if folderID != "" {
		if err = checkFileName(folderID); err != nil {
			return nil, err
		}
	}

	tags := parseTags(tagsStr)

	return &DocMetadata{
		Type:       docType,
		Name:       name,
		FolderID:   folderID,
		Tags:       tags,
		Executable: executable,
	}, nil
}

// Upload is the method for uploading a file
//
// This will be used to upload a file
// @TODO
func Upload(metadata *DocMetadata, storage afero.Fs, body io.ReadCloser) error {
	if metadata.Type != FileDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	defer body.Close()
	return afero.SafeWriteReader(storage, path, body)
}

// CreateDirectory is the method for creating a new directory
//
// @TODO
func CreateDirectory(metadata *DocMetadata, storage afero.Fs) error {
	if metadata.Type != FolderDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	exists, err := afero.DirExists(storage, path)
	if err != nil {
		return err
	}
	if exists {
		return errDocAlreadyExists
	}

	return storage.Mkdir(path, 0777)
}

// CreationHandler handle all POST requests on /files/:folder-id
// aiming at creating a new document in the FS. Given the Type
// parameter of the request, it will either upload a new file or
// create a new directory.
//
// swagger:route POST /files/:folder-id files uploadFileOrCreateDir
func CreationHandler(c *gin.Context) {
	instance, err := middlewares.GetInstance(c)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	storage, err := instance.GetStorageProvider()
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	metadata, err := NewDocMetadata(
		c.Query("Type"),
		c.Query("Name"),
		c.Param("folder-id"),
		c.Query("Tags"),
		c.Query("Executable") == "true",
	)

	if err != nil {
		c.AbortWithError(http.StatusUnprocessableEntity, err)
		return
	}

	contentType := c.ContentType()
	if contentType == "" {
		contentType = DefaultContentType
	}

	exists, err := checkParentFolderID(storage, metadata.FolderID)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	if !exists {
		err = fmt.Errorf("Parent folder with given FolderID does not exist")
		c.AbortWithError(http.StatusNotFound, err)
		return
	}

	fmt.Printf("%s:\n\t- %+v\n\t- %v\n", metadata.Name, metadata, contentType)

	switch metadata.Type {
	case FileDocType:
		err = Upload(metadata, storage, c.Request.Body)
	case FolderDocType:
		err = CreateDirectory(metadata, storage)
	}

	if err != nil {
		var code int
		switch err {
		case errDocAlreadyExists:
			code = http.StatusConflict
		default:
			code = http.StatusInternalServerError
		}
		c.AbortWithError(code, err)
		return
	}

	data := []byte{'O', 'K'}
	c.Data(http.StatusCreated, jsonapi.ContentType, data)
}

// Routes sets the routing for the files service
func Routes(router *gin.RouterGroup) {
	router.POST("/", CreationHandler)
	router.POST("/:folder-id", CreationHandler)
}

func parseTags(str string) []string {
	var tags []string
	for _, tag := range strings.Split(str, ",") {
		// @TODO: more sanitization maybe ?
		tag = strings.TrimSpace(tag)
		if tag != "" {
			tags = append(tags, tag)
		}
	}
	return tags
}

func parseDocType(docType string) (DocType, error) {
	var result DocType
	var err error
	switch docType {
	case "io.cozy.files":
		result = FileDocType
	case "io.cozy.folders":
		result = FolderDocType
	default:
		err = errDocTypeInvalid
	}
	return result, err
}

func checkFileName(str string) error {
	if str == "" || regFileName.MatchString(str) {
		return errIllegalFilename
	}
	return nil
}

func checkParentFolderID(storage afero.Fs, folderID string) (bool, error) {
	if folderID == "" {
		return true, nil
	}

	exists, err := afero.DirExists(storage, folderID)
	if err != nil {
		return false, err
	}
	if !exists {
		return false, nil
	}

	return true, nil
}
<|endoftext|>


<commit_before>package alert

import (
	"appengine"
	"appengine/datastore"
)

const ALERT_KEY string = "ALERT"

type ALERT_STATE int

const ( // iota is reset to 0
	UNKNOWN ALERT_STATE = iota // c0 == 0
	OK      ALERT_STATE = iota // c1 == 1
	WARN    ALERT_STATE = iota // c2 == 2
	ERROR   ALERT_STATE = iota // c3 == 3
)

type Alert struct {
	//Must be unique within project
	Name string

	//The unqiue Project Name that this alert is for
	Project string `json:"-"`

	//A human readable escription of the alert
	Description string

	//The target string to fetch the data
	Target string

	// The level at which to change alert status to a warning
	// Note:
	// if ErrorLevel is higher than WarnLevel than alert is fired when value exceedes ErrorLevel
	// If ErrorLevel is lower than warnLevel than alert is fired when value goes below ErrorLevel
	WarnLevel float64

	// The level at which to change alert status to an error and send alert to subscriptions
	// Note:
	// if ErrorLevel is higher than WarnLevel than alert is fired when value exceedes ErrorLevel
	// If ErrorLevel is lower than warnLevel than alert is fired when value goes below ErrorLevel
	ErrorLevel float64

	// The previous state of the Alert
	PreviousState ALERT_STATE `json:"-"`
}

// Checkis if there is a change to the alert status. This functions returns if the status has changed as
// well as what the previous and current values of the status are.
func (this *Alert) CheckAlertStatusChange(value float64) (changed bool, previousState ALERT_STATE, currentState ALERT_STATE) {

	currentState = this.getCurrentState(value)
	previousState = this.PreviousState
	changed = this.PreviousState != currentState
	return
}

func (this *Alert) getCurrentState(value float64) ALERT_STATE {
	if this.ErrorLevel >= this.WarnLevel {
		return this.checkHighBad(value)
	} else {
		return this.checkLowBad(value)
	}
}

func (this *Alert) checkHighBad(value float64) ALERT_STATE {
	if value >= this.ErrorLevel {
		return ERROR
	} else if value >= this.WarnLevel {
		return WARN
	} else {
		return OK
	}
}

func (this *Alert) checkLowBad(value float64) ALERT_STATE {
	if value <= this.ErrorLevel {
		return ERROR
	} else if value <= this.WarnLevel {
		return WARN
	} else {
		return OK
	}
}

func GetAlertsFromGAE(projectId string, context appengine.Context) ([]Alert, error) {
	query := datastore.NewQuery(ALERT_KEY).Filter("Project =", projectId)
	alerts := make([]Alert, 0)
	_, err := query.GetAll(context, &alerts)
	return alerts, err
}

func GetAlertFromGAE(projectId string, alertId string, context appengine.Context) (Alert, error) {
	var alert Alert
	err := datastore.Get(context, datastore.NewKey(context,
		ALERT_KEY, projectId+"-"+alertId, 0, nil), &alert)
	return alert, err
}

func SaveAlertToGAE(alert Alert, context appengine.Context) error {
	_, err := datastore.Put(context, datastore.NewKey(context, ALERT_KEY,
		alert.Project+"-"+alert.Name, 0, nil), &alert)
	return err
}
<commit_msg>Fixing typo<commit_after>package alert

import (
	"appengine"
	"appengine/datastore"
)

const ALERT_KEY string = "ALERT"

type ALERT_STATE int

const ( // iota is reset to 0
	UNKNOWN ALERT_STATE = iota // c0 == 0
	OK      ALERT_STATE = iota // c1 == 1
	WARN    ALERT_STATE = iota // c2 == 2
	ERROR   ALERT_STATE = iota // c3 == 3
)

type Alert struct {
	//Must be unique within project
	Name string

	//The unqiue Project Name that this alert is for
	Project string `json:"-"`

	//A human readable escription of the alert
	Description string

	//The target string to fetch the data
	Target string

	// The level at which to change alert status to a warning
	// Note:
	// if ErrorLevel is higher than WarnLevel than alert is fired when value exceedes ErrorLevel
	// If ErrorLevel is lower than warnLevel than alert is fired when value goes below ErrorLevel
	WarnLevel float64

	// The level at which to change alert status to an error and send alert to subscriptions
	// Note:
	// if ErrorLevel is higher than WarnLevel than alert is fired when value exceedes ErrorLevel
	// If ErrorLevel is lower than warnLevel than alert is fired when value goes below ErrorLevel
	ErrorLevel float64

	// The previous state of the Alert
	PreviousState ALERT_STATE `json:"-"`
}

// Checks if there is a change to the alert status. This functions returns if the status has changed as
// well as what the previous and current values of the status are.
func (this *Alert) CheckAlertStatusChange(value float64) (changed bool, previousState ALERT_STATE, currentState ALERT_STATE) {

	currentState = this.getCurrentState(value)
	previousState = this.PreviousState
	changed = this.PreviousState != currentState
	return
}

func (this *Alert) getCurrentState(value float64) ALERT_STATE {
	if this.ErrorLevel >= this.WarnLevel {
		return this.checkHighBad(value)
	} else {
		return this.checkLowBad(value)
	}
}

func (this *Alert) checkHighBad(value float64) ALERT_STATE {
	if value >= this.ErrorLevel {
		return ERROR
	} else if value >= this.WarnLevel {
		return WARN
	} else {
		return OK
	}
}

func (this *Alert) checkLowBad(value float64) ALERT_STATE {
	if value <= this.ErrorLevel {
		return ERROR
	} else if value <= this.WarnLevel {
		return WARN
	} else {
		return OK
	}
}

func GetAlertsFromGAE(projectId string, context appengine.Context) ([]Alert, error) {
	query := datastore.NewQuery(ALERT_KEY).Filter("Project =", projectId)
	alerts := make([]Alert, 0)
	_, err := query.GetAll(context, &alerts)
	return alerts, err
}

func GetAlertFromGAE(projectId string, alertId string, context appengine.Context) (Alert, error) {
	var alert Alert
	err := datastore.Get(context, datastore.NewKey(context,
		ALERT_KEY, projectId+"-"+alertId, 0, nil), &alert)
	return alert, err
}

func SaveAlertToGAE(alert Alert, context appengine.Context) error {
	_, err := datastore.Put(context, datastore.NewKey(context, ALERT_KEY,
		alert.Project+"-"+alert.Name, 0, nil), &alert)
	return err
}
<|endoftext|>


<commit_before>package replicaset

import (
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/juju/loggo"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// MaxPeers defines the maximum number of peers that mongo supports.
const MaxPeers = 7

var logger = loggo.GetLogger("juju.replicaset")

// Initiate sets up a replica set with the given replica set name with the
// single given member.  It need be called only once for a given mongo replica
// set.  The tags specified will be added as tags on the member that is created
// in the replica set.
//
// Note that you must set DialWithInfo and set Direct = true when dialing into a
// specific non-initiated mongo server.
//
// See http://docs.mongodb.org/manual/reference/method/rs.initiate/ for more
// details.
func Initiate(session *mgo.Session, address, name string, tags map[string]string) error {
	monotonicSession := session.Clone()
	monotonicSession.SetMode(mgo.Monotonic, true)
	cfg := Config{
		Name:    name,
		Version: 1,
		Members: []Member{{
			Id:      1,
			Address: address,
			Tags:    tags,
		}},
	}
	logger.Infof("Initiating replicaset with config %#v", cfg)
	return monotonicSession.Run(bson.D{{"replSetInitiate", cfg}}, nil)
}

// Member holds configuration information for a replica set member.
//
// See http://docs.mongodb.org/manual/reference/replica-configuration/
// for more details
type Member struct {
	// Id is a unique id for a member in a set.
	Id int `bson:"_id"`

	// Address holds the network address of the member,
	// in the form hostname:port.
	Address string `bson:"host"`

	// Arbiter holds whether the member is an arbiter only.
	// This value is optional; it defaults to false.
	Arbiter *bool `bson:"arbiterOnly,omitempty"`

	// BuildIndexes determines whether the mongod builds indexes on this member.
	// This value is optional; it defaults to true.
	BuildIndexes *bool `bson:"buildIndexes,omitempty"`

	// Hidden determines whether the replica set hides this member from
	// the output of IsMaster.
	// This value is optional; it defaults to false.
	Hidden *bool `bson:"hidden,omitempty"`

	// Priority determines eligibility of a member to become primary.
	// This value is optional; it defaults to 1.
	Priority *float64 `bson:"priority,omitempty"`

	// Tags store additional information about a replica member, often used for
	// customizing read preferences and write concern.
	Tags map[string]string `bson:"tags,omitempty"`

	// SlaveDelay describes the number of seconds behind the master that this
	// replica set member should lag rounded up to the nearest second.
	// This value is optional; it defaults to 0.
	SlaveDelay *time.Duration `bson:"slaveDelay,omitempty"`

	// Votes controls the number of votes a server has in a replica set election.
	// This value is optional; it defaults to 1.
	Votes *int `bson:"votes,omitempty"`
}

func fmtConfigForLog(config *Config) string {
	memberInfo := make([]string, len(config.Members))
	for i, member := range config.Members {
		memberInfo[i] = fmt.Sprintf("Member{%d %q %v}", member.Id, member.Address, member.Tags)

	}
	return fmt.Sprintf("{Name: %s, Version: %d, Members: {%s}}",
		config.Name, config.Version, strings.Join(memberInfo, ", "))
}

// applyRelSetConfig applies the new config to the mongo session. It also logs
// what the changes are. It checks if the replica set changes cause the DB
// connection to be dropped. If so, it Refreshes the session and tries to Ping
// again.
func applyRelSetConfig(cmd string, session *mgo.Session, oldconfig, newconfig *Config) error {
	logger.Debugf("%s() changing replica set\nfrom %s\n  to %s",
		cmd, fmtConfigForLog(oldconfig), fmtConfigForLog(newconfig))
	err := session.Run(bson.D{{"replSetReconfig", newconfig}}, nil)
	// We will only try to Ping 2 times
	for i := 0; i < 2; i++ {
		if err == io.EOF {
			// If the primary changes due to replSetReconfig, then all
			// current connections are dropped.
			// Refreshing should fix us up.
			logger.Debugf("got EOF while running %s(), calling session.Refresh()", cmd)
			session.Refresh()
		} else if err != nil {
			// For all errors that aren't EOF, return immediately
			return err
		}
		// err is either nil or EOF and we called Refresh, so Ping to
		// make sure we're actually connected
		err = session.Ping()
		// Change the command because it is the new command we ran
		cmd = "Ping"
	}
	return err
}

// Add adds the given members to the session's replica set.  Duplicates of
// existing replicas will be ignored.
//
// Members will have their Ids set automatically if they are not already > 0
func Add(session *mgo.Session, members ...Member) error {
	config, err := CurrentConfig(session)
	if err != nil {
		return err
	}

	oldconfig := *config
	config.Version++
	max := 0
	for _, member := range config.Members {
		if member.Id > max {
			max = member.Id
		}
	}

outerLoop:
	for _, newMember := range members {
		for _, member := range config.Members {
			if member.Address == newMember.Address {
				// already exists, skip it
				continue outerLoop
			}
		}
		// let the caller specify an id if they want, treat zero as unspecified
		if newMember.Id < 1 {
			max++
			newMember.Id = max
		}
		config.Members = append(config.Members, newMember)
	}
	return applyRelSetConfig("Add", session, &oldconfig, config)
}

// Remove removes members with the given addresses from the replica set. It is
// not an error to remove addresses of non-existent replica set members.
func Remove(session *mgo.Session, addrs ...string) error {
	config, err := CurrentConfig(session)
	if err != nil {
		return err
	}
	oldconfig := *config
	config.Version++
	for _, rem := range addrs {
		for n, repl := range config.Members {
			if repl.Address == rem {
				config.Members = append(config.Members[:n], config.Members[n+1:]...)
				break
			}
		}
	}
	return applyRelSetConfig("Remove", session, &oldconfig, config)
}

// Set changes the current set of replica set members.  Members will have their
// ids set automatically if their ids are not already > 0.
func Set(session *mgo.Session, members []Member) error {
	config, err := CurrentConfig(session)
	if err != nil {
		return err
	}

	// Copy the current configuration for logging
	oldconfig := *config
	config.Version++

	// Assign ids to members that did not previously exist, starting above the
	// value of the highest id that already existed
	ids := map[string]int{}
	max := 0
	for _, m := range config.Members {
		ids[m.Address] = m.Id
		if m.Id > max {
			max = m.Id
		}
	}

	for x, m := range members {
		if id, ok := ids[m.Address]; ok {
			m.Id = id
		} else if m.Id < 1 {
			max++
			m.Id = max
		}
		members[x] = m
	}

	config.Members = members

	return applyRelSetConfig("Set", session, &oldconfig, config)
}

// Config reports information about the configuration of a given mongo node
type IsMasterResults struct {
	// The following fields hold information about the specific mongodb node.
	IsMaster  bool      `bson:"ismaster"`
	Secondary bool      `bson:"secondary"`
	Arbiter   bool      `bson:"arbiterOnly"`
	Address   string    `bson:"me"`
	LocalTime time.Time `bson:"localTime"`

	// The following fields hold information about the replica set.
	ReplicaSetName string   `bson:"setName"`
	Addresses      []string `bson:"hosts"`
	Arbiters       []string `bson:"arbiters"`
	PrimaryAddress string   `bson:"primary"`
}

// IsMaster returns information about the configuration of the node that
// the given session is connected to.
func IsMaster(session *mgo.Session) (*IsMasterResults, error) {
	results := &IsMasterResults{}
	err := session.Run("isMaster", results)
	if err != nil {
		return nil, err
	}
	return results, nil
}

var ErrMasterNotConfigured = fmt.Errorf("mongo master not configured")

// MasterHostPort returns the "address:port" string for the primary
// mongo server in the replicaset. It returns ErrMasterNotConfigured if
// the replica set has not yet been initiated.
func MasterHostPort(session *mgo.Session) (string, error) {
	results, err := IsMaster(session)
	if err != nil {
		return "", err
	}
	if results.PrimaryAddress == "" {
		return "", ErrMasterNotConfigured
	}
	return results.PrimaryAddress, nil
}

// CurrentMembers returns the current members of the replica set.
func CurrentMembers(session *mgo.Session) ([]Member, error) {
	cfg, err := CurrentConfig(session)
	if err != nil {
		return nil, err
	}
	return cfg.Members, nil
}

// CurrentConfig returns the Config for the given session's replica set.  If
// there is no current config, the error returned will be mgo.ErrNotFound.
func CurrentConfig(session *mgo.Session) (*Config, error) {
	cfg := &Config{}
	monotonicSession := session.Clone()
	monotonicSession.SetMode(mgo.Monotonic, true)
	err := monotonicSession.DB("local").C("system.replset").Find(nil).One(cfg)
	if err == mgo.ErrNotFound {
		return nil, err
	}
	if err != nil {
		return nil, fmt.Errorf("cannot get replset config: %s", err.Error())
	}
	return cfg, nil
}

// Config is the document stored in mongodb that defines the servers in the
// replica set
type Config struct {
	Name    string   `bson:"_id"`
	Version int      `bson:"version"`
	Members []Member `bson:"members"`
}

// CurrentStatus returns the status of the replica set for the given session.
func CurrentStatus(session *mgo.Session) (*Status, error) {
	status := &Status{}
	err := session.Run("replSetGetStatus", status)
	if err != nil {
		return nil, fmt.Errorf("cannot get replica set status: %v", err)
	}
	return status, nil
}

// Status holds data about the status of members of the replica set returned
// from replSetGetStatus
//
// See http://docs.mongodb.org/manual/reference/command/replSetGetStatus/#dbcmd.replSetGetStatus
type Status struct {
	Name    string         `bson:"set"`
	Members []MemberStatus `bson:"members"`
}

// Status holds the status of a replica set member returned from
// replSetGetStatus.
type MemberStatus struct {
	// Id holds the replica set id of the member that the status is describing.
	Id int `bson:"_id"`

	// Address holds address of the member that the status is describing.
	Address string `bson:"name"`

	// Self holds whether this is the status for the member that
	// the session is connected to.
	Self bool `bson:"self"`

	// ErrMsg holds the most recent error or status message received
	// from the member.
	ErrMsg string `bson:"errmsg"`

	// Healthy reports whether the member is up. It is true for the
	// member that the request was made to.
	Healthy bool `bson:"health"`

	// State describes the current state of the member.
	State MemberState `bson:"state"`

	// Uptime describes how long the member has been online.
	Uptime time.Duration `bson:"uptime"`

	// Ping describes the length of time a round-trip packet takes to travel
	// between the remote member and the local instance.  It is zero for the
	// member that the session is connected to.
	Ping time.Duration `bson:"pingMS"`
}

// MemberState represents the state of a replica set member.
// See http://docs.mongodb.org/manual/reference/replica-states/
type MemberState int

const (
	StartupState = iota
	PrimaryState
	SecondaryState
	RecoveringState
	FatalState
	Startup2State
	UnknownState
	ArbiterState
	DownState
	RollbackState
	ShunnedState
)

var memberStateStrings = []string{
	StartupState:    "STARTUP",
	PrimaryState:    "PRIMARY",
	SecondaryState:  "SECONDARY",
	RecoveringState: "RECOVERING",
	FatalState:      "FATAL",
	Startup2State:   "STARTUP2",
	UnknownState:    "UNKNOWN",
	ArbiterState:    "ARBITER",
	DownState:       "DOWN",
	RollbackState:   "ROLLBACK",
	ShunnedState:    "SHUNNED",
}

// String returns a string describing the state.
func (state MemberState) String() string {
	if state < 0 || int(state) >= len(memberStateStrings) {
		return "INVALID_MEMBER_STATE"
	}
	return memberStateStrings[state]
}
<commit_msg>replicaset: close cloned sessions<commit_after>package replicaset

import (
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/juju/loggo"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// MaxPeers defines the maximum number of peers that mongo supports.
const MaxPeers = 7

var logger = loggo.GetLogger("juju.replicaset")

// Initiate sets up a replica set with the given replica set name with the
// single given member.  It need be called only once for a given mongo replica
// set.  The tags specified will be added as tags on the member that is created
// in the replica set.
//
// Note that you must set DialWithInfo and set Direct = true when dialing into a
// specific non-initiated mongo server.
//
// See http://docs.mongodb.org/manual/reference/method/rs.initiate/ for more
// details.
func Initiate(session *mgo.Session, address, name string, tags map[string]string) error {
	monotonicSession := session.Clone()
	defer monotonicSession.Close()
	monotonicSession.SetMode(mgo.Monotonic, true)
	cfg := Config{
		Name:    name,
		Version: 1,
		Members: []Member{{
			Id:      1,
			Address: address,
			Tags:    tags,
		}},
	}
	logger.Infof("Initiating replicaset with config %#v", cfg)
	return monotonicSession.Run(bson.D{{"replSetInitiate", cfg}}, nil)
}

// Member holds configuration information for a replica set member.
//
// See http://docs.mongodb.org/manual/reference/replica-configuration/
// for more details
type Member struct {
	// Id is a unique id for a member in a set.
	Id int `bson:"_id"`

	// Address holds the network address of the member,
	// in the form hostname:port.
	Address string `bson:"host"`

	// Arbiter holds whether the member is an arbiter only.
	// This value is optional; it defaults to false.
	Arbiter *bool `bson:"arbiterOnly,omitempty"`

	// BuildIndexes determines whether the mongod builds indexes on this member.
	// This value is optional; it defaults to true.
	BuildIndexes *bool `bson:"buildIndexes,omitempty"`

	// Hidden determines whether the replica set hides this member from
	// the output of IsMaster.
	// This value is optional; it defaults to false.
	Hidden *bool `bson:"hidden,omitempty"`

	// Priority determines eligibility of a member to become primary.
	// This value is optional; it defaults to 1.
	Priority *float64 `bson:"priority,omitempty"`

	// Tags store additional information about a replica member, often used for
	// customizing read preferences and write concern.
	Tags map[string]string `bson:"tags,omitempty"`

	// SlaveDelay describes the number of seconds behind the master that this
	// replica set member should lag rounded up to the nearest second.
	// This value is optional; it defaults to 0.
	SlaveDelay *time.Duration `bson:"slaveDelay,omitempty"`

	// Votes controls the number of votes a server has in a replica set election.
	// This value is optional; it defaults to 1.
	Votes *int `bson:"votes,omitempty"`
}

func fmtConfigForLog(config *Config) string {
	memberInfo := make([]string, len(config.Members))
	for i, member := range config.Members {
		memberInfo[i] = fmt.Sprintf("Member{%d %q %v}", member.Id, member.Address, member.Tags)

	}
	return fmt.Sprintf("{Name: %s, Version: %d, Members: {%s}}",
		config.Name, config.Version, strings.Join(memberInfo, ", "))
}

// applyRelSetConfig applies the new config to the mongo session. It also logs
// what the changes are. It checks if the replica set changes cause the DB
// connection to be dropped. If so, it Refreshes the session and tries to Ping
// again.
func applyRelSetConfig(cmd string, session *mgo.Session, oldconfig, newconfig *Config) error {
	logger.Debugf("%s() changing replica set\nfrom %s\n  to %s",
		cmd, fmtConfigForLog(oldconfig), fmtConfigForLog(newconfig))
	err := session.Run(bson.D{{"replSetReconfig", newconfig}}, nil)
	// We will only try to Ping 2 times
	for i := 0; i < 2; i++ {
		if err == io.EOF {
			// If the primary changes due to replSetReconfig, then all
			// current connections are dropped.
			// Refreshing should fix us up.
			logger.Debugf("got EOF while running %s(), calling session.Refresh()", cmd)
			session.Refresh()
		} else if err != nil {
			// For all errors that aren't EOF, return immediately
			return err
		}
		// err is either nil or EOF and we called Refresh, so Ping to
		// make sure we're actually connected
		err = session.Ping()
		// Change the command because it is the new command we ran
		cmd = "Ping"
	}
	return err
}

// Add adds the given members to the session's replica set.  Duplicates of
// existing replicas will be ignored.
//
// Members will have their Ids set automatically if they are not already > 0
func Add(session *mgo.Session, members ...Member) error {
	config, err := CurrentConfig(session)
	if err != nil {
		return err
	}

	oldconfig := *config
	config.Version++
	max := 0
	for _, member := range config.Members {
		if member.Id > max {
			max = member.Id
		}
	}

outerLoop:
	for _, newMember := range members {
		for _, member := range config.Members {
			if member.Address == newMember.Address {
				// already exists, skip it
				continue outerLoop
			}
		}
		// let the caller specify an id if they want, treat zero as unspecified
		if newMember.Id < 1 {
			max++
			newMember.Id = max
		}
		config.Members = append(config.Members, newMember)
	}
	return applyRelSetConfig("Add", session, &oldconfig, config)
}

// Remove removes members with the given addresses from the replica set. It is
// not an error to remove addresses of non-existent replica set members.
func Remove(session *mgo.Session, addrs ...string) error {
	config, err := CurrentConfig(session)
	if err != nil {
		return err
	}
	oldconfig := *config
	config.Version++
	for _, rem := range addrs {
		for n, repl := range config.Members {
			if repl.Address == rem {
				config.Members = append(config.Members[:n], config.Members[n+1:]...)
				break
			}
		}
	}
	return applyRelSetConfig("Remove", session, &oldconfig, config)
}

// Set changes the current set of replica set members.  Members will have their
// ids set automatically if their ids are not already > 0.
func Set(session *mgo.Session, members []Member) error {
	config, err := CurrentConfig(session)
	if err != nil {
		return err
	}

	// Copy the current configuration for logging
	oldconfig := *config
	config.Version++

	// Assign ids to members that did not previously exist, starting above the
	// value of the highest id that already existed
	ids := map[string]int{}
	max := 0
	for _, m := range config.Members {
		ids[m.Address] = m.Id
		if m.Id > max {
			max = m.Id
		}
	}

	for x, m := range members {
		if id, ok := ids[m.Address]; ok {
			m.Id = id
		} else if m.Id < 1 {
			max++
			m.Id = max
		}
		members[x] = m
	}

	config.Members = members

	return applyRelSetConfig("Set", session, &oldconfig, config)
}

// Config reports information about the configuration of a given mongo node
type IsMasterResults struct {
	// The following fields hold information about the specific mongodb node.
	IsMaster  bool      `bson:"ismaster"`
	Secondary bool      `bson:"secondary"`
	Arbiter   bool      `bson:"arbiterOnly"`
	Address   string    `bson:"me"`
	LocalTime time.Time `bson:"localTime"`

	// The following fields hold information about the replica set.
	ReplicaSetName string   `bson:"setName"`
	Addresses      []string `bson:"hosts"`
	Arbiters       []string `bson:"arbiters"`
	PrimaryAddress string   `bson:"primary"`
}

// IsMaster returns information about the configuration of the node that
// the given session is connected to.
func IsMaster(session *mgo.Session) (*IsMasterResults, error) {
	results := &IsMasterResults{}
	err := session.Run("isMaster", results)
	if err != nil {
		return nil, err
	}
	return results, nil
}

var ErrMasterNotConfigured = fmt.Errorf("mongo master not configured")

// MasterHostPort returns the "address:port" string for the primary
// mongo server in the replicaset. It returns ErrMasterNotConfigured if
// the replica set has not yet been initiated.
func MasterHostPort(session *mgo.Session) (string, error) {
	results, err := IsMaster(session)
	if err != nil {
		return "", err
	}
	if results.PrimaryAddress == "" {
		return "", ErrMasterNotConfigured
	}
	return results.PrimaryAddress, nil
}

// CurrentMembers returns the current members of the replica set.
func CurrentMembers(session *mgo.Session) ([]Member, error) {
	cfg, err := CurrentConfig(session)
	if err != nil {
		return nil, err
	}
	return cfg.Members, nil
}

// CurrentConfig returns the Config for the given session's replica set.  If
// there is no current config, the error returned will be mgo.ErrNotFound.
func CurrentConfig(session *mgo.Session) (*Config, error) {
	cfg := &Config{}
	monotonicSession := session.Clone()
	defer monotonicSession.Close()
	monotonicSession.SetMode(mgo.Monotonic, true)
	err := monotonicSession.DB("local").C("system.replset").Find(nil).One(cfg)
	if err == mgo.ErrNotFound {
		return nil, err
	}
	if err != nil {
		return nil, fmt.Errorf("cannot get replset config: %s", err.Error())
	}
	return cfg, nil
}

// Config is the document stored in mongodb that defines the servers in the
// replica set
type Config struct {
	Name    string   `bson:"_id"`
	Version int      `bson:"version"`
	Members []Member `bson:"members"`
}

// CurrentStatus returns the status of the replica set for the given session.
func CurrentStatus(session *mgo.Session) (*Status, error) {
	status := &Status{}
	err := session.Run("replSetGetStatus", status)
	if err != nil {
		return nil, fmt.Errorf("cannot get replica set status: %v", err)
	}
	return status, nil
}

// Status holds data about the status of members of the replica set returned
// from replSetGetStatus
//
// See http://docs.mongodb.org/manual/reference/command/replSetGetStatus/#dbcmd.replSetGetStatus
type Status struct {
	Name    string         `bson:"set"`
	Members []MemberStatus `bson:"members"`
}

// Status holds the status of a replica set member returned from
// replSetGetStatus.
type MemberStatus struct {
	// Id holds the replica set id of the member that the status is describing.
	Id int `bson:"_id"`

	// Address holds address of the member that the status is describing.
	Address string `bson:"name"`

	// Self holds whether this is the status for the member that
	// the session is connected to.
	Self bool `bson:"self"`

	// ErrMsg holds the most recent error or status message received
	// from the member.
	ErrMsg string `bson:"errmsg"`

	// Healthy reports whether the member is up. It is true for the
	// member that the request was made to.
	Healthy bool `bson:"health"`

	// State describes the current state of the member.
	State MemberState `bson:"state"`

	// Uptime describes how long the member has been online.
	Uptime time.Duration `bson:"uptime"`

	// Ping describes the length of time a round-trip packet takes to travel
	// between the remote member and the local instance.  It is zero for the
	// member that the session is connected to.
	Ping time.Duration `bson:"pingMS"`
}

// MemberState represents the state of a replica set member.
// See http://docs.mongodb.org/manual/reference/replica-states/
type MemberState int

const (
	StartupState = iota
	PrimaryState
	SecondaryState
	RecoveringState
	FatalState
	Startup2State
	UnknownState
	ArbiterState
	DownState
	RollbackState
	ShunnedState
)

var memberStateStrings = []string{
	StartupState:    "STARTUP",
	PrimaryState:    "PRIMARY",
	SecondaryState:  "SECONDARY",
	RecoveringState: "RECOVERING",
	FatalState:      "FATAL",
	Startup2State:   "STARTUP2",
	UnknownState:    "UNKNOWN",
	ArbiterState:    "ARBITER",
	DownState:       "DOWN",
	RollbackState:   "ROLLBACK",
	ShunnedState:    "SHUNNED",
}

// String returns a string describing the state.
func (state MemberState) String() string {
	if state < 0 || int(state) >= len(memberStateStrings) {
		return "INVALID_MEMBER_STATE"
	}
	return memberStateStrings[state]
}
<|endoftext|>


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

package repository

import (
	"bytes"
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/log"
	"io"
)

// Unit interface represents a unit of execution.
//
// It must provide two methods:
//
//   * GetName: returns the name of the unit.
//   * Command: runs a command in the unit.
//
// Whatever that has a name and is able to run commands, is a unit.
type Unit interface {
	GetName() string
	Command(stdout, stderr io.Writer, cmd ...string) error
}

// Clone runs a git clone to clone the app repository in a unit.
//
// Given a machine id (from juju), it runs a git clone into this machine,
// cloning from the bare repository that is being served by git-daemon in the
// tsuru server.
func clone(u Unit) ([]byte, error) {
	var buf bytes.Buffer
	cmd := fmt.Sprintf("git clone %s /home/application/current --depth 1", GetReadOnlyUrl(u.GetName()))
	err := u.Command(&buf, &buf, cmd)
	b := buf.Bytes()
	log.Printf(`"git clone" output: %s`, b)
	return b, err
}

// Pull runs a git pull to update the code in a unit.
//
// It works like Clone, pulling from the app bare repository.
func pull(u Unit) ([]byte, error) {
	var buf bytes.Buffer
	cmd := fmt.Sprintf("cd /home/application/current && git pull origin master")
	err := u.Command(&buf, &buf, cmd)
	b := buf.Bytes()
	log.Printf(`"git pull" output: %s`, b)
	return b, err
}

// CloneOrPull runs a git clone or a git pull in a unit of the app.
//
// First it tries to clone, and if the clone fail (meaning that the repository
// is already cloned), it pulls changes from the bare repository.
func CloneOrPull(u Unit) ([]byte, error) {
	b, err := clone(u)
	if err != nil {
		b, err = pull(u)
	}
	return b, err
}

// getGitServer returns the git server defined in the tsuru.conf file.
//
// If it is not defined, this function panics.
func getGitServer() string {
	gitServer, err := config.GetString("git:server")
	if err != nil {
		panic(err)
	}
	return gitServer
}

// GetUrl returns the ssh clone-url from an app.
func GetUrl(app string) string {
	return fmt.Sprintf("git@%s:%s.git", getGitServer(), app)
}

// GetReadOnlyUrl returns the ssh url for communication with git-daemon.
func GetReadOnlyUrl(app string) string {
	return fmt.Sprintf("git://%s/%s.git", getGitServer(), app)
}

// GetPath returns the path to the repository where the app code is in its
// units.
func GetPath() (string, error) {
	return config.GetString("git:unit-repo")
}
<commit_msg>repository: fixing repository url by removing the protocol<commit_after>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package repository

import (
	"bytes"
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/log"
	"io"
	"strings"
)

// Unit interface represents a unit of execution.
//
// It must provide two methods:
//
//   * GetName: returns the name of the unit.
//   * Command: runs a command in the unit.
//
// Whatever that has a name and is able to run commands, is a unit.
type Unit interface {
	GetName() string
	Command(stdout, stderr io.Writer, cmd ...string) error
}

// Clone runs a git clone to clone the app repository in a unit.
//
// Given a machine id (from juju), it runs a git clone into this machine,
// cloning from the bare repository that is being served by git-daemon in the
// tsuru server.
func clone(u Unit) ([]byte, error) {
	var buf bytes.Buffer
	cmd := fmt.Sprintf("git clone %s /home/application/current --depth 1", GetReadOnlyUrl(u.GetName()))
	err := u.Command(&buf, &buf, cmd)
	b := buf.Bytes()
	log.Printf(`"git clone" output: %s`, b)
	return b, err
}

// Pull runs a git pull to update the code in a unit.
//
// It works like Clone, pulling from the app bare repository.
func pull(u Unit) ([]byte, error) {
	var buf bytes.Buffer
	cmd := fmt.Sprintf("cd /home/application/current && git pull origin master")
	err := u.Command(&buf, &buf, cmd)
	b := buf.Bytes()
	log.Printf(`"git pull" output: %s`, b)
	return b, err
}

// CloneOrPull runs a git clone or a git pull in a unit of the app.
//
// First it tries to clone, and if the clone fail (meaning that the repository
// is already cloned), it pulls changes from the bare repository.
func CloneOrPull(u Unit) ([]byte, error) {
	b, err := clone(u)
	if err != nil {
		b, err = pull(u)
	}
	return b, err
}

// getGitServer returns the git server defined in the tsuru.conf file.
//
// If it is not defined, this function panics.
func getGitServer() string {
	gitServer, err := config.GetString("git:server")
	if err != nil {
		panic(err)
	}
	return gitServer
}

// GetUrl returns the ssh clone-url from an app.
func GetUrl(app string) string {
	s := strings.Replace(getGitServer(), "http://", "", -1) // https?
	return fmt.Sprintf("git@%s:%s.git", s, app)
}

// GetReadOnlyUrl returns the ssh url for communication with git-daemon.
func GetReadOnlyUrl(app string) string {
	s := strings.Replace(getGitServer(), "http://", "", -1) // https?
	return fmt.Sprintf("git://%s/%s.git", s, app)
}

// GetPath returns the path to the repository where the app code is in its
// units.
func GetPath() (string, error) {
	return config.GetString("git:unit-repo")
}
<|endoftext|>


<commit_before>package main

import (
	"./arch"
	"./loader"
)

type Usercorn struct {
	*Unicorn
	loader    loader.Loader
	Entry     uint64
	OS        string
	StackBase uint64
}

func NewUsercorn(exe string) (*Usercorn, error) {
	l, err := loader.LoadFile(exe)
	if err != nil {
		return nil, err
	}
	a, err := arch.GetArch(l.Arch(), l.OS())
	if err != nil {
		return nil, err
	}
	uc, err := NewUnicorn(a)
	if err != nil {
		return nil, err
	}
	u := &Usercorn{
		Unicorn: uc,
		loader:  l,
		OS:      l.OS(),
		Entry:   l.Entry(),
	}
	return u, nil
}

func (u *Usercorn) Run(args ...string) error {
	if err := u.mapMemory(); err != nil {
		return err
	}
	if err := u.setupStack(); err != nil {
		return err
	}
	// envp
	u.Push(0)
	// argv
	if err := u.pushStrings(args...); err != nil {
		return err
	}
	// argc
	u.Push(uint64(len(args)))
	return u.Uc.Start(u.Entry, 0)
}

func (u *Usercorn) mapMemory() error {
	segments, err := u.loader.Segments()
	if err != nil {
		return err
	}
	for _, seg := range segments {
		if err := u.MemMap(seg.Addr, uint64(len(seg.Data))); err != nil {
			return err
		}
		if err := u.MemWrite(seg.Addr, seg.Data); err != nil {
			return err
		}
	}
	return nil
}

func (u *Usercorn) setupStack() error {
	stack, err := u.Mmap(STACK_BASE, STACK_SIZE)
	if err != nil {
		return err
	}
	u.StackBase = stack
	if err := u.RegWrite(u.Arch.SP, stack+STACK_SIZE); err != nil {
		return err
	}
	return nil
}

func (u *Usercorn) pushStrings(args ...string) error {
	argvSize := 0
	for _, v := range args {
		argvSize += len(v) + 1
	}
	argvAddr, err := u.Mmap(0, uint64(argvSize))
	if err != nil {
		return err
	}
	buf := make([]byte, argvSize)
	addrs := make([]uint64, 0, len(args)+1)
	var pos uint64
	for i := len(args) - 1; i >= 0; i-- {
		copy(buf[pos:], []byte(args[i]))
		addrs = append(addrs, argvAddr+pos)
		pos += uint64(len(args[i]) + 1)
	}
	u.MemWrite(argvAddr, buf)
	u.Push(0)
	for _, v := range addrs {
		u.Push(v)
	}
	return nil
}
<commit_msg>print entry point and stack debug<commit_after>package main

import (
	"encoding/hex"
	"fmt"

	"./arch"
	"./loader"
)

type Usercorn struct {
	*Unicorn
	loader    loader.Loader
	Entry     uint64
	OS        string
	StackBase uint64
}

func NewUsercorn(exe string) (*Usercorn, error) {
	l, err := loader.LoadFile(exe)
	if err != nil {
		return nil, err
	}
	a, err := arch.GetArch(l.Arch(), l.OS())
	if err != nil {
		return nil, err
	}
	uc, err := NewUnicorn(a)
	if err != nil {
		return nil, err
	}
	u := &Usercorn{
		Unicorn: uc,
		loader:  l,
		OS:      l.OS(),
		Entry:   l.Entry(),
	}
	return u, nil
}

func (u *Usercorn) Run(args ...string) error {
	if err := u.mapMemory(); err != nil {
		return err
	}
	if err := u.setupStack(); err != nil {
		return err
	}
	// envp
	u.Push(0)
	// argv
	if err := u.pushStrings(args...); err != nil {
		return err
	}
	// argc
	u.Push(uint64(len(args)))

	fmt.Println("[entry point]")
	mem, err := u.MemRead(u.Entry, 64)
	if err != nil {
		fmt.Println(err)
	} else {
		dis, err := Disas(mem, u.Entry, u.Arch)
		if err != nil {
			fmt.Println(err)
		} else {
			fmt.Println(dis)
		}
	}
	sp, err := u.RegRead(u.Arch.SP)
	if err != nil {
		return err
	}
	buf := make([]byte, u.StackBase+STACK_SIZE-sp)
	if err := u.MemReadInto(buf, sp); err != nil {
		return err
	}
	fmt.Println("[initial stack]", hex.EncodeToString(buf[:]))

	fmt.Println("=====================================")
	fmt.Println("==== Program output begins here. ====")
	fmt.Println("=====================================")
	return u.Uc.Start(u.Entry, 0)
}

func (u *Usercorn) mapMemory() error {
	segments, err := u.loader.Segments()
	if err != nil {
		return err
	}
	for _, seg := range segments {
		if err := u.MemMap(seg.Addr, uint64(len(seg.Data))); err != nil {
			return err
		}
		if err := u.MemWrite(seg.Addr, seg.Data); err != nil {
			return err
		}
	}
	return nil
}

func (u *Usercorn) setupStack() error {
	stack, err := u.Mmap(STACK_BASE, STACK_SIZE)
	if err != nil {
		return err
	}
	u.StackBase = stack
	if err := u.RegWrite(u.Arch.SP, stack+STACK_SIZE); err != nil {
		return err
	}
	return nil
}

func (u *Usercorn) pushStrings(args ...string) error {
	argvSize := 0
	for _, v := range args {
		argvSize += len(v) + 1
	}
	argvAddr, err := u.Mmap(0, uint64(argvSize))
	if err != nil {
		return err
	}
	buf := make([]byte, argvSize)
	addrs := make([]uint64, 0, len(args)+1)
	var pos uint64
	for i := len(args) - 1; i >= 0; i-- {
		copy(buf[pos:], []byte(args[i]))
		addrs = append(addrs, argvAddr+pos)
		pos += uint64(len(args[i]) + 1)
	}
	u.MemWrite(argvAddr, buf)
	u.Push(0)
	for _, v := range addrs {
		u.Push(v)
	}
	return nil
}
<|endoftext|>


<commit_before>// Backend.go
package web2tcp

import (
	"crypto/tls"
	"log"
	"net"
)

type BackendOptions struct {
	Endpoint      string
	ProxyProtocol bool
	Tls           struct {
		Enabled           bool
		SkipVerify        bool
		CertAuthorityFile string
	}
}

type Backend struct {
	options *BackendOptions
}

func NewBackend(options *BackendOptions) *Backend {
	backend := &Backend{
		options: options,
	}

	return backend
}

func (b *Backend) NewSession() (Session, error) {
	tlsConfig := &tls.Config{
		InsecureSkipVerify: b.options.Tls.SkipVerify,
	}

	var conn net.Conn
	var err error

	if b.options.Tls.Enabled {
		conn, err = tls.Dial("tcp", b.options.Endpoint, tlsConfig)
	} else {
		conn, err = net.Dial("tcp", b.options.Endpoint)
	}

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

	return newTcpSession(conn), err
}
<commit_msg>Added support for reading Root CA's from file<commit_after>// Backend.go
package web2tcp

import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"log"
	"net"
)

type BackendOptions struct {
	Endpoint      string
	ProxyProtocol bool
	Tls           struct {
		Enabled           bool
		SkipVerify        bool
		CertAuthorityFile string
	}
}

type Backend struct {
	options   *BackendOptions
	tlsConfig *tls.Config
}

func NewBackend(options *BackendOptions) *Backend {
	tlsConfig := &tls.Config{
		InsecureSkipVerify: options.Tls.SkipVerify,
	}

	if !options.Tls.SkipVerify {
		bytes, err := ioutil.ReadFile(options.Tls.CertAuthorityFile)

		if err != nil {
			log.Fatalln("Unable to read Certificate Authorities file.")
		}

		rootCAs := x509.NewCertPool()

		ok := rootCAs.AppendCertsFromPEM(bytes)

		if !ok {
			panic("Failed to parse Certificate Authorities from file.")
		}

		tlsConfig.RootCAs = rootCAs
	}

	backend := &Backend{
		options:   options,
		tlsConfig: tlsConfig,
	}

	return backend
}

func (b *Backend) NewSession() (Session, error) {

	var conn net.Conn
	var err error

	if b.options.Tls.Enabled {
		conn, err = tls.Dial("tcp", b.options.Endpoint, b.tlsConfig)
	} else {
		conn, err = net.Dial("tcp", b.options.Endpoint)
	}

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

	return newTcpSession(conn), err
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The TestGrid Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package util has convenience functions for use throughout TestGrid.
package util

import (
	"context"
	"time"

	"github.com/sirupsen/logrus"
)

// Progress log every duration, including an ETA for completion.
// Returns a function for updating the current index
func Progress(ctx context.Context, log logrus.FieldLogger, every time.Duration, total int, msg string) func(int) {
	start := time.Now()
	ch := make(chan int, 1)
	go func() {
		timer := time.NewTimer(every)
		defer timer.Stop()
		var current int
		for {
			select {
			case <-ctx.Done():
				return
			case current = <-ch:
				// updated index
			case now := <-timer.C:
				elapsed := now.Sub(start)
				rate := elapsed / time.Duration(current)
				eta := time.Duration(total-current) * rate

				log.WithFields(logrus.Fields{
					"current": current,
					"total":   total,
					"percent": (100 * current) / total,
					"remain":  eta.Round(time.Minute),
					"eta":     now.Add(eta).Round(time.Minute),
					"start":   start.Round(time.Minute),
				}).Info(msg)
				timer.Reset(every)
			}
		}
	}()

	return func(idx int) {
		select {
		case ch <- idx:
		default:
		}
	}
}
<commit_msg>Prevent divide by zero calculating rate<commit_after>/*
Copyright 2021 The TestGrid Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package util has convenience functions for use throughout TestGrid.
package util

import (
	"context"
	"time"

	"github.com/sirupsen/logrus"
)

// Progress log every duration, including an ETA for completion.
// Returns a function for updating the current index
func Progress(ctx context.Context, log logrus.FieldLogger, every time.Duration, total int, msg string) func(int) {
	start := time.Now()
	ch := make(chan int, 1)
	go func() {
		timer := time.NewTimer(every)
		defer timer.Stop()
		var current int
		for {
			select {
			case <-ctx.Done():
				return
			case current = <-ch:
				// updated index
			case now := <-timer.C:
				elapsed := now.Sub(start)
				var rate time.Duration
				if current > 0 {
					rate = elapsed / time.Duration(current)
				}
				eta := time.Duration(total-current) * rate

				log.WithFields(logrus.Fields{
					"current": current,
					"total":   total,
					"percent": (100 * current) / total,
					"remain":  eta.Round(time.Minute),
					"eta":     now.Add(eta).Round(time.Minute),
					"start":   start.Round(time.Minute),
				}).Info(msg)
				timer.Reset(every)
			}
		}
	}()

	return func(idx int) {
		select {
		case ch <- idx:
		default:
		}
	}
}
<|endoftext|>


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

package util

import (
	"bytes"
	"crypto/tls"
	"io"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"

	"golang.org/x/net/http2"

	"github.com/cockroachdb/cockroach/util/stop"
)

type replayableConn struct {
	net.Conn
	buf    bytes.Buffer
	reader io.Reader
}

// Do not call `replay` more than once, bad things will happen.
func (bc *replayableConn) replay() *replayableConn {
	bc.reader = io.MultiReader(&bc.buf, bc.Conn)
	return bc
}

func (bc *replayableConn) Read(b []byte) (int, error) {
	return bc.reader.Read(b)
}

func newBufferedConn(conn net.Conn) *replayableConn {
	bc := replayableConn{Conn: conn}
	bc.reader = io.TeeReader(conn, &bc.buf)
	return &bc
}

type replayableConnListener struct {
	net.Listener
}

func (ml *replayableConnListener) Accept() (net.Conn, error) {
	conn, err := ml.Listener.Accept()
	if err == nil {
		conn = newBufferedConn(conn)
	}
	return conn, err
}

// Listen delegates to `net.Listen` and, if tlsConfig is not nil, to `tls.NewListener`.
// The returned listener's Addr() method will return an address with the hostname unresovled,
// which means it can be used to initiate TLS connections.
func Listen(addr net.Addr, tlsConfig *tls.Config) (net.Listener, error) {
	ln, err := net.Listen(addr.Network(), addr.String())
	if err == nil {
		if tlsConfig != nil {
			ln = tls.NewListener(ln, tlsConfig)
		} else {
			ln = &replayableConnListener{ln}
		}
	}

	return ln, err
}

// ListenAndServe creates a listener and serves handler on it, closing
// the listener when signalled by the stopper.
func ListenAndServe(stopper *stop.Stopper, handler http.Handler, addr net.Addr, tlsConfig *tls.Config) (net.Listener, error) {
	ln, err := Listen(addr, tlsConfig)
	if err != nil {
		return nil, err
	}

	var mu sync.Mutex
	activeConns := make(map[net.Conn]struct{})

	httpServer := http.Server{
		TLSConfig: tlsConfig,
		Handler:   handler,
		ConnState: func(conn net.Conn, state http.ConnState) {
			mu.Lock()
			switch state {
			case http.StateNew:
				activeConns[conn] = struct{}{}
			case http.StateClosed:
				delete(activeConns, conn)
			}
			mu.Unlock()
		},
	}

	var http2Server http2.Server

	if tlsConfig == nil {
		connOpts := http2.ServeConnOpts{
			BaseConfig: &httpServer,
			Handler:    handler,
		}

		httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.ProtoMajor == 2 {
				if conn, _, err := w.(http.Hijacker).Hijack(); err == nil {
					http2Server.ServeConn(conn.(*replayableConn).replay(), &connOpts)
				} else {
					log.Fatal(err)
				}
			} else {
				handler.ServeHTTP(w, r)
			}
		})
	}

	if err := http2.ConfigureServer(&httpServer, &http2Server); err != nil {
		return nil, err
	}

	stopper.RunWorker(func() {
		<-stopper.ShouldDrain()
		// Some unit tests manually close `ln`, so it may already be closed
		// when we get here.
		if err := ln.Close(); err != nil && !IsClosedConnection(err) {
			log.Fatal(err)
		}
	})

	stopper.RunWorker(func() {
		if err := httpServer.Serve(ln); err != nil && !IsClosedConnection(err) {
			log.Fatal(err)
		}

		<-stopper.ShouldStop()

		mu.Lock()
		for conn := range activeConns {
			conn.Close()
		}
		mu.Unlock()
	})

	return ln, nil
}

// IsClosedConnection returns true if err is the net package's errClosed.
func IsClosedConnection(err error) bool {
	return strings.Contains(err.Error(), "use of closed network connection")
}
<commit_msg>util: removed outdated comment<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Tamir Duberstein (tamird@gmail.com)

package util

import (
	"bytes"
	"crypto/tls"
	"io"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"

	"golang.org/x/net/http2"

	"github.com/cockroachdb/cockroach/util/stop"
)

type replayableConn struct {
	net.Conn
	buf    bytes.Buffer
	reader io.Reader
}

// Do not call `replay` more than once, bad things will happen.
func (bc *replayableConn) replay() *replayableConn {
	bc.reader = io.MultiReader(&bc.buf, bc.Conn)
	return bc
}

func (bc *replayableConn) Read(b []byte) (int, error) {
	return bc.reader.Read(b)
}

func newBufferedConn(conn net.Conn) *replayableConn {
	bc := replayableConn{Conn: conn}
	bc.reader = io.TeeReader(conn, &bc.buf)
	return &bc
}

type replayableConnListener struct {
	net.Listener
}

func (ml *replayableConnListener) Accept() (net.Conn, error) {
	conn, err := ml.Listener.Accept()
	if err == nil {
		conn = newBufferedConn(conn)
	}
	return conn, err
}

// Listen delegates to `net.Listen` and, if tlsConfig is not nil, to `tls.NewListener`.
func Listen(addr net.Addr, tlsConfig *tls.Config) (net.Listener, error) {
	ln, err := net.Listen(addr.Network(), addr.String())
	if err == nil {
		if tlsConfig != nil {
			ln = tls.NewListener(ln, tlsConfig)
		} else {
			ln = &replayableConnListener{ln}
		}
	}

	return ln, err
}

// ListenAndServe creates a listener and serves handler on it, closing
// the listener when signalled by the stopper.
func ListenAndServe(stopper *stop.Stopper, handler http.Handler, addr net.Addr, tlsConfig *tls.Config) (net.Listener, error) {
	ln, err := Listen(addr, tlsConfig)
	if err != nil {
		return nil, err
	}

	var mu sync.Mutex
	activeConns := make(map[net.Conn]struct{})

	httpServer := http.Server{
		TLSConfig: tlsConfig,
		Handler:   handler,
		ConnState: func(conn net.Conn, state http.ConnState) {
			mu.Lock()
			switch state {
			case http.StateNew:
				activeConns[conn] = struct{}{}
			case http.StateClosed:
				delete(activeConns, conn)
			}
			mu.Unlock()
		},
	}

	var http2Server http2.Server

	if tlsConfig == nil {
		connOpts := http2.ServeConnOpts{
			BaseConfig: &httpServer,
			Handler:    handler,
		}

		httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.ProtoMajor == 2 {
				if conn, _, err := w.(http.Hijacker).Hijack(); err == nil {
					http2Server.ServeConn(conn.(*replayableConn).replay(), &connOpts)
				} else {
					log.Fatal(err)
				}
			} else {
				handler.ServeHTTP(w, r)
			}
		})
	}

	if err := http2.ConfigureServer(&httpServer, &http2Server); err != nil {
		return nil, err
	}

	stopper.RunWorker(func() {
		<-stopper.ShouldDrain()
		// Some unit tests manually close `ln`, so it may already be closed
		// when we get here.
		if err := ln.Close(); err != nil && !IsClosedConnection(err) {
			log.Fatal(err)
		}
	})

	stopper.RunWorker(func() {
		if err := httpServer.Serve(ln); err != nil && !IsClosedConnection(err) {
			log.Fatal(err)
		}

		<-stopper.ShouldStop()

		mu.Lock()
		for conn := range activeConns {
			conn.Close()
		}
		mu.Unlock()
	})

	return ln, nil
}

// IsClosedConnection returns true if err is the net package's errClosed.
func IsClosedConnection(err error) bool {
	return strings.Contains(err.Error(), "use of closed network connection")
}
<|endoftext|>