<commit_before>package edit

import (
	"errors"
	"os"
	"path"
	"strings"
	"unicode/utf8"

	"github.com/elves/elvish/parse"
)

// Navigation subsystem.

// Interface.

type navigation struct {
	current    *navColumn
	parent     *navColumn
	dirPreview *navColumn
	showHidden bool
	filtering  bool
	filter     string
}

func (*navigation) Mode() ModeType {
	return modeNavigation
}

func (n *navigation) ModeLine(width int) *buffer {
	s := " NAVIGATING "
	if n.showHidden {
		s += "(show hidden) "
	}
	b := newBuffer(width)
	b.writes(TrimWcWidth(s, width), styleForMode)
	b.writes(" ", "")
	b.writes(n.filter, styleForFilter)
	b.dot = b.cursor()
	return b
}

func startNav(ed *Editor) {
	initNavigation(&ed.navigation)
	ed.mode = &ed.navigation
}

func navUp(ed *Editor) {
	ed.navigation.prev()
}

func navDown(ed *Editor) {
	ed.navigation.next()
}

func navPageUp(ed *Editor) {
	ed.navigation.current.pageUp()
	ed.navigation.refresh()
}

func navPageDown(ed *Editor) {
	ed.navigation.current.pageDown()
	ed.navigation.refresh()
}

func navLeft(ed *Editor) {
	ed.navigation.ascend()
}

func navRight(ed *Editor) {
	ed.navigation.descend()
}

func navTriggerShowHidden(ed *Editor) {
	ed.navigation.showHidden = !ed.navigation.showHidden
	ed.navigation.refresh()
}

func navTriggerFilter(ed *Editor) {
	ed.navigation.filtering = !ed.navigation.filtering
}

func navInsertSelected(ed *Editor) {
	ed.insertAtDot(parse.Quote(ed.navigation.current.selectedName()) + " ")
}

func navigationDefault(ed *Editor) {
	// Use key binding for insert mode without exiting nigation mode.
	k := ed.lastKey
	n := &ed.navigation
	if n.filtering && likeChar(k) {
		n.filter += k.String()
		n.refreshCurrent()
	} else if n.filtering && k == (Key{Backspace, 0}) {
		_, size := utf8.DecodeLastRuneInString(n.filter)
		if size > 0 {
			n.filter = n.filter[:len(n.filter)-size]
			n.refreshCurrent()
		}
	} else if f, ok := keyBindings[modeInsert][k]; ok {
		ed.CallFn(f)
	} else {
		ed.CallFn(keyBindings[modeInsert][Default])
	}
}

// Implementation.
// TODO(xiaq): Support file preview in navigation mode
// TODO(xiaq): Remember which file was selected in each directory.

var (
	errorEmptyCwd      = errors.New("current directory is empty")
	errorNoCwdInParent = errors.New("could not find current directory in ..")
)

func initNavigation(n *navigation) {
	*n = navigation{}
	n.refresh()
}

func (n *navigation) maintainSelected(name string) {
	n.current.selected = 0
	for i, s := range n.current.candidates {
		if s.text > name {
			break
		}
		n.current.selected = i
	}
}

func (n *navigation) refreshCurrent() {
	selectedName := n.current.selectedName()
	all, err := n.loaddir(".")
	if err != nil {
		n.current = newErrNavColumn(err)
		return
	}
	// Try to select the old selected file.
	// XXX(xiaq): This would break when we support alternative ordering.
	n.current = newNavColumn(all, func(i int) bool {
		return i == 0 || all[i].text <= selectedName
	})
	n.current.changeFilter(n.filter)
	n.maintainSelected(selectedName)
}

func (n *navigation) refreshParent() {
	wd, err := os.Getwd()
	if err != nil {
		n.parent = newErrNavColumn(err)
		return
	}
	if wd == "/" {
		n.parent = newNavColumn(nil, nil)
	} else {
		all, err := n.loaddir("..")
		if err != nil {
			n.parent = newErrNavColumn(err)
			return
		}
		cwd, err := os.Stat(".")
		if err != nil {
			n.parent = newErrNavColumn(err)
			return
		}
		n.parent = newNavColumn(all, func(i int) bool {
			d, _ := os.Lstat("../" + all[i].text)
			return os.SameFile(d, cwd)
		})
	}
}

func (n *navigation) refreshDirPreview() {
	if n.current.selected != -1 {
		name := n.current.selectedName()
		fi, err := os.Stat(name)
		if err != nil {
			n.dirPreview = newErrNavColumn(err)
			return
		}
		if fi.Mode().IsDir() {
			all, err := n.loaddir(name)
			if err != nil {
				n.dirPreview = newErrNavColumn(err)
				return
			}
			n.dirPreview = newNavColumn(all, func(int) bool { return false })
		} else {
			// TODO(xiaq): Support regular file preview in navigation mode
			n.dirPreview = nil
		}
	} else {
		n.dirPreview = nil
	}
}

// refresh rereads files in current and parent directories and maintains the
// selected file if possible.
func (n *navigation) refresh() {
	n.refreshCurrent()
	n.refreshParent()
	n.refreshDirPreview()
}

// ascend changes current directory to the parent.
// TODO(xiaq): navigation.{ascend descend} bypasses the cd builtin. This can be
// problematic if cd acquires more functionality (e.g. trigger a hook).
func (n *navigation) ascend() error {
	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	if wd == "/" {
		return nil
	}

	name := n.parent.selectedName()
	err = os.Chdir("..")
	if err != nil {
		return err
	}
	n.filter = ""
	n.refresh()
	n.maintainSelected(name)
	// XXX Refresh dir preview again. We should perhaps not have used refresh
	// above.
	n.refreshDirPreview()
	return nil
}

// descend changes current directory to the selected file, if it is a
// directory.
func (n *navigation) descend() error {
	if n.current.selected == -1 {
		return errorEmptyCwd
	}
	name := n.current.selectedName()
	err := os.Chdir(name)
	if err != nil {
		return err
	}
	n.filter = ""
	n.current.selected = -1
	n.refresh()
	n.refreshDirPreview()
	return nil
}

// prev selects the previous file.
func (n *navigation) prev() {
	if n.current.selected > 0 {
		n.current.selected--
	}
	n.refresh()
}

// next selects the next file.
func (n *navigation) next() {
	if n.current.selected != -1 && n.current.selected < len(n.current.candidates)-1 {
		n.current.selected++
	}
	n.refresh()
}

func (n *navigation) loaddir(dir string) ([]styled, error) {
	f, err := os.Open(dir)
	if err != nil {
		return nil, err
	}
	infos, err := f.Readdir(0)
	if err != nil {
		return nil, err
	}
	var all []styled
	for _, info := range infos {
		if n.showHidden || info.Name()[0] != '.' {
			name := info.Name()
			all = append(all, styled{name, defaultLsColor.getStyle(path.Join(dir, name))})
		}
	}
	sortStyleds(all)

	return all, nil
}

const (
	navigationListingColMargin          = 1
	navigationListingMinWidthForPadding = 5

	parentColumnWeight  = 3.0
	currentColumnWeight = 8.0
	previewColumnWeight = 9.0
	columnWeightsSum    = parentColumnWeight + currentColumnWeight + previewColumnWeight
)

func (nav *navigation) List(width, maxHeight int) *buffer {
	margin := navigationListingColMargin

	w := width - margin*2
	ws := distributeWidths(w,
		[]float64{
			parentColumnWeight, currentColumnWeight, previewColumnWeight},
		[]int{
			nav.parent.FullWidth(maxHeight),
			nav.current.FullWidth(maxHeight),
			nav.dirPreview.FullWidth(maxHeight),
		})
	wParent, wCurrent, wPreview := ws[0], ws[1], ws[2]

	b := nav.parent.List(wParent, maxHeight)

	bCurrent := nav.current.List(wCurrent, maxHeight)
	b.extendHorizontal(bCurrent, wParent+margin)

	if wPreview > 0 {
		bPreview := nav.dirPreview.List(wPreview, maxHeight)
		b.extendHorizontal(bPreview, wParent+wCurrent+2*margin)
	}

	return b
}

// navColumn is a column in the navigation layout.
type navColumn struct {
	listing
	all        []styled
	candidates []styled
	// selected int
	err error
}

func newNavColumn(all []styled, sel func(int) bool) *navColumn {
	nc := &navColumn{all: all, candidates: all}
	nc.provider = nc
	nc.selected = -1
	for i := range all {
		if sel(i) {
			nc.selected = i
		}
	}
	return nc
}

func newErrNavColumn(err error) *navColumn {
	nc := &navColumn{err: err}
	nc.provider = nc
	return nc
}

func (nc *navColumn) Placeholder() string {
	if nc.err != nil {
		return nc.err.Error()
	}
	return ""
}

func (nc *navColumn) Len() int {
	return len(nc.candidates)
}

func (nc *navColumn) Show(i, w int) styled {
	s := nc.candidates[i]
	if w >= navigationListingMinWidthForPadding {
		return styled{" " + ForceWcWidth(s.text, w-2), s.style}
	}
	return styled{ForceWcWidth(s.text, w), s.style}
}

func (nc *navColumn) Filter(filter string) int {
	nc.candidates = nc.candidates[:0]
	for _, s := range nc.all {
		if strings.Contains(s.text, filter) {
			nc.candidates = append(nc.candidates, s)
		}
	}
	return 0
}

func (nc *navColumn) FullWidth(h int) int {
	if nc == nil {
		return 0
	}
	maxw := 0
	for _, s := range nc.candidates {
		maxw = max(maxw, WcWidths(s.text))
	}
	if maxw >= navigationListingMinWidthForPadding {
		maxw += 2
	}
	if len(nc.candidates) > h {
		maxw++
	}
	return maxw
}

func (nc *navColumn) Accept(i int, ed *Editor) {
	// TODO
}

func (nc *navColumn) ModeTitle(i int) string {
	// Not used
	return ""
}

func (nc *navColumn) selectedName() string {
	if nc == nil || nc.selected == -1 || nc.selected >= len(nc.candidates) {
		return ""
	}
	return nc.candidates[nc.selected].text
}
<commit_msg>Refresh dir preview when changing filter in nav mode.<commit_after>package edit

import (
	"errors"
	"os"
	"path"
	"strings"
	"unicode/utf8"

	"github.com/elves/elvish/parse"
)

// Navigation subsystem.

// Interface.

type navigation struct {
	current    *navColumn
	parent     *navColumn
	dirPreview *navColumn
	showHidden bool
	filtering  bool
	filter     string
}

func (*navigation) Mode() ModeType {
	return modeNavigation
}

func (n *navigation) ModeLine(width int) *buffer {
	s := " NAVIGATING "
	if n.showHidden {
		s += "(show hidden) "
	}
	b := newBuffer(width)
	b.writes(TrimWcWidth(s, width), styleForMode)
	b.writes(" ", "")
	b.writes(n.filter, styleForFilter)
	b.dot = b.cursor()
	return b
}

func startNav(ed *Editor) {
	initNavigation(&ed.navigation)
	ed.mode = &ed.navigation
}

func navUp(ed *Editor) {
	ed.navigation.prev()
}

func navDown(ed *Editor) {
	ed.navigation.next()
}

func navPageUp(ed *Editor) {
	ed.navigation.current.pageUp()
	ed.navigation.refresh()
}

func navPageDown(ed *Editor) {
	ed.navigation.current.pageDown()
	ed.navigation.refresh()
}

func navLeft(ed *Editor) {
	ed.navigation.ascend()
}

func navRight(ed *Editor) {
	ed.navigation.descend()
}

func navTriggerShowHidden(ed *Editor) {
	ed.navigation.showHidden = !ed.navigation.showHidden
	ed.navigation.refresh()
}

func navTriggerFilter(ed *Editor) {
	ed.navigation.filtering = !ed.navigation.filtering
}

func navInsertSelected(ed *Editor) {
	ed.insertAtDot(parse.Quote(ed.navigation.current.selectedName()) + " ")
}

func navigationDefault(ed *Editor) {
	// Use key binding for insert mode without exiting nigation mode.
	k := ed.lastKey
	n := &ed.navigation
	if n.filtering && likeChar(k) {
		n.filter += k.String()
		n.refreshCurrent()
		n.refreshDirPreview()
	} else if n.filtering && k == (Key{Backspace, 0}) {
		_, size := utf8.DecodeLastRuneInString(n.filter)
		if size > 0 {
			n.filter = n.filter[:len(n.filter)-size]
			n.refreshCurrent()
			n.refreshDirPreview()
		}
	} else if f, ok := keyBindings[modeInsert][k]; ok {
		ed.CallFn(f)
	} else {
		ed.CallFn(keyBindings[modeInsert][Default])
	}
}

// Implementation.
// TODO(xiaq): Support file preview in navigation mode
// TODO(xiaq): Remember which file was selected in each directory.

var (
	errorEmptyCwd      = errors.New("current directory is empty")
	errorNoCwdInParent = errors.New("could not find current directory in ..")
)

func initNavigation(n *navigation) {
	*n = navigation{}
	n.refresh()
}

func (n *navigation) maintainSelected(name string) {
	n.current.selected = 0
	for i, s := range n.current.candidates {
		if s.text > name {
			break
		}
		n.current.selected = i
	}
}

func (n *navigation) refreshCurrent() {
	selectedName := n.current.selectedName()
	all, err := n.loaddir(".")
	if err != nil {
		n.current = newErrNavColumn(err)
		return
	}
	// Try to select the old selected file.
	// XXX(xiaq): This would break when we support alternative ordering.
	n.current = newNavColumn(all, func(i int) bool {
		return i == 0 || all[i].text <= selectedName
	})
	n.current.changeFilter(n.filter)
	n.maintainSelected(selectedName)
}

func (n *navigation) refreshParent() {
	wd, err := os.Getwd()
	if err != nil {
		n.parent = newErrNavColumn(err)
		return
	}
	if wd == "/" {
		n.parent = newNavColumn(nil, nil)
	} else {
		all, err := n.loaddir("..")
		if err != nil {
			n.parent = newErrNavColumn(err)
			return
		}
		cwd, err := os.Stat(".")
		if err != nil {
			n.parent = newErrNavColumn(err)
			return
		}
		n.parent = newNavColumn(all, func(i int) bool {
			d, _ := os.Lstat("../" + all[i].text)
			return os.SameFile(d, cwd)
		})
	}
}

func (n *navigation) refreshDirPreview() {
	if n.current.selected != -1 {
		name := n.current.selectedName()
		fi, err := os.Stat(name)
		if err != nil {
			n.dirPreview = newErrNavColumn(err)
			return
		}
		if fi.Mode().IsDir() {
			all, err := n.loaddir(name)
			if err != nil {
				n.dirPreview = newErrNavColumn(err)
				return
			}
			n.dirPreview = newNavColumn(all, func(int) bool { return false })
		} else {
			// TODO(xiaq): Support regular file preview in navigation mode
			n.dirPreview = nil
		}
	} else {
		n.dirPreview = nil
	}
}

// refresh rereads files in current and parent directories and maintains the
// selected file if possible.
func (n *navigation) refresh() {
	n.refreshCurrent()
	n.refreshParent()
	n.refreshDirPreview()
}

// ascend changes current directory to the parent.
// TODO(xiaq): navigation.{ascend descend} bypasses the cd builtin. This can be
// problematic if cd acquires more functionality (e.g. trigger a hook).
func (n *navigation) ascend() error {
	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	if wd == "/" {
		return nil
	}

	name := n.parent.selectedName()
	err = os.Chdir("..")
	if err != nil {
		return err
	}
	n.filter = ""
	n.refresh()
	n.maintainSelected(name)
	// XXX Refresh dir preview again. We should perhaps not have used refresh
	// above.
	n.refreshDirPreview()
	return nil
}

// descend changes current directory to the selected file, if it is a
// directory.
func (n *navigation) descend() error {
	if n.current.selected == -1 {
		return errorEmptyCwd
	}
	name := n.current.selectedName()
	err := os.Chdir(name)
	if err != nil {
		return err
	}
	n.filter = ""
	n.current.selected = -1
	n.refresh()
	n.refreshDirPreview()
	return nil
}

// prev selects the previous file.
func (n *navigation) prev() {
	if n.current.selected > 0 {
		n.current.selected--
	}
	n.refresh()
}

// next selects the next file.
func (n *navigation) next() {
	if n.current.selected != -1 && n.current.selected < len(n.current.candidates)-1 {
		n.current.selected++
	}
	n.refresh()
}

func (n *navigation) loaddir(dir string) ([]styled, error) {
	f, err := os.Open(dir)
	if err != nil {
		return nil, err
	}
	infos, err := f.Readdir(0)
	if err != nil {
		return nil, err
	}
	var all []styled
	for _, info := range infos {
		if n.showHidden || info.Name()[0] != '.' {
			name := info.Name()
			all = append(all, styled{name, defaultLsColor.getStyle(path.Join(dir, name))})
		}
	}
	sortStyleds(all)

	return all, nil
}

const (
	navigationListingColMargin          = 1
	navigationListingMinWidthForPadding = 5

	parentColumnWeight  = 3.0
	currentColumnWeight = 8.0
	previewColumnWeight = 9.0
	columnWeightsSum    = parentColumnWeight + currentColumnWeight + previewColumnWeight
)

func (nav *navigation) List(width, maxHeight int) *buffer {
	margin := navigationListingColMargin

	w := width - margin*2
	ws := distributeWidths(w,
		[]float64{
			parentColumnWeight, currentColumnWeight, previewColumnWeight},
		[]int{
			nav.parent.FullWidth(maxHeight),
			nav.current.FullWidth(maxHeight),
			nav.dirPreview.FullWidth(maxHeight),
		})
	wParent, wCurrent, wPreview := ws[0], ws[1], ws[2]

	b := nav.parent.List(wParent, maxHeight)

	bCurrent := nav.current.List(wCurrent, maxHeight)
	b.extendHorizontal(bCurrent, wParent+margin)

	if wPreview > 0 {
		bPreview := nav.dirPreview.List(wPreview, maxHeight)
		b.extendHorizontal(bPreview, wParent+wCurrent+2*margin)
	}

	return b
}

// navColumn is a column in the navigation layout.
type navColumn struct {
	listing
	all        []styled
	candidates []styled
	// selected int
	err error
}

func newNavColumn(all []styled, sel func(int) bool) *navColumn {
	nc := &navColumn{all: all, candidates: all}
	nc.provider = nc
	nc.selected = -1
	for i := range all {
		if sel(i) {
			nc.selected = i
		}
	}
	return nc
}

func newErrNavColumn(err error) *navColumn {
	nc := &navColumn{err: err}
	nc.provider = nc
	return nc
}

func (nc *navColumn) Placeholder() string {
	if nc.err != nil {
		return nc.err.Error()
	}
	return ""
}

func (nc *navColumn) Len() int {
	return len(nc.candidates)
}

func (nc *navColumn) Show(i, w int) styled {
	s := nc.candidates[i]
	if w >= navigationListingMinWidthForPadding {
		return styled{" " + ForceWcWidth(s.text, w-2), s.style}
	}
	return styled{ForceWcWidth(s.text, w), s.style}
}

func (nc *navColumn) Filter(filter string) int {
	nc.candidates = nc.candidates[:0]
	for _, s := range nc.all {
		if strings.Contains(s.text, filter) {
			nc.candidates = append(nc.candidates, s)
		}
	}
	return 0
}

func (nc *navColumn) FullWidth(h int) int {
	if nc == nil {
		return 0
	}
	maxw := 0
	for _, s := range nc.candidates {
		maxw = max(maxw, WcWidths(s.text))
	}
	if maxw >= navigationListingMinWidthForPadding {
		maxw += 2
	}
	if len(nc.candidates) > h {
		maxw++
	}
	return maxw
}

func (nc *navColumn) Accept(i int, ed *Editor) {
	// TODO
}

func (nc *navColumn) ModeTitle(i int) string {
	// Not used
	return ""
}

func (nc *navColumn) selectedName() string {
	if nc == nil || nc.selected == -1 || nc.selected >= len(nc.candidates) {
		return ""
	}
	return nc.candidates[nc.selected].text
}
<|endoftext|>


<commit_before>// tgotop project main.go

package main

import (
	"bytes"
	"fmt"
	//	spew "github.com/davecgh/go-spew/spew"
	ui "github.com/gizak/termui"
	//tm "github.com/nsf/termbox-go"
	"os"
	"os/signal"
	"syscall"
	"text/tabwriter"
	"time"
)

const (
	mult  = 10
	dtick = time.Second / 2 //refreshing interval
	rtick = time.Second /60 //redrawint interval
	atick = time.Second     //averaging interval
	ptick = atick / mult    //polling interval
)

func main() {
	err := ui.Init()
	if err != nil {
		panic(err)
	}
	defer ui.Close()

	qMess := ui.NewPar(":PRESS q TO QUIT")
	qMess.Height = 3

	gSwap := ui.NewGauge()
	gSwap.Height = 3

	gMem := ui.NewGauge()
	gMem.Height = 3

	gNet := ui.NewList()

	//getting ready to close stuff on command
	evt := ui.EventCh()
	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGABRT, syscall.SIGTERM)

	nd := new(netData)
	err = nd.Init(3*mult, ptick)

	if err != nil {
		panic(err)
	}

	gNet.Height = nd.size + 3
	var m memData

	go func() {
		for {
			err := m.Update()
			if err != nil {
				panic(err)
			}

			gMem.Percent = m.memPercent
			gMem.Border.Label = fillfmt("Memory", m.memUse, m.memTotal)

			gSwap.Percent = m.swapPercent
			gSwap.Border.Label = fillfmt("Swap", m.swapUse, m.swapTotal)

			gNet.Items = netf(nd)
			time.Sleep(dtick)
		}
	}()

	ui.Body.AddRows(
		ui.NewRow(
			ui.NewCol(6, 0, gMem, gSwap),
			ui.NewCol(6, 0, gNet)),
		ui.NewRow(ui.NewCol(12, 0, qMess)))

	ui.Body.Align()
	tkr := time.Tick(rtick)
	for {
		select {
		case e := <-evt:
			if dealwithevents(e) {
				return
			}
		case <-sig:
			return
		case <- tkr:
			ui.Render(ui.Body)
		}
	}
}

func fillfmt(s string, u uint64, t uint64) string {
	return fmt.Sprintf("%v used: %v / %v", s, humanBytes(float32(u)), humanBytes(float32(t)))
}

func dealwithevents(e ui.Event) bool {
	if e.Type == ui.EventKey && (e.Ch == 'q' || e.Ch == 'Q') {
		return true
	}
	if e.Type == ui.EventKey && e.Key == ui.KeyCtrlC {
		return true
	}
	if e.Type == ui.EventResize {
		ui.Body.Width = ui.TermWidth()
		ui.Body.Align()
	}
	return false
}

func netf(nd *netData) []string {
	strings := make([]string, 0, nd.size+1)
	var b bytes.Buffer
	tb := tabwriter.NewWriter(&b, 10, 8, 0, ' ', tabwriter.AlignRight)
	fmt.Fprintln(tb, "IFace\t Down\t Up\t")
	for i := 0; i < nd.size; i++ {
		fmt.Fprintf(tb, "%v:\t %s/s\t %s/s\t\n",
			nd.name[i],
			humanBytes(nd.GetD(i, mult)),
			humanBytes(nd.GetU(i, mult)))
	}

	err := tb.Flush()

	if err != nil {
		panic(err)
	}
	for i := 0; i <= nd.size; i++ {
		ts, err := b.ReadString('\n')
		if err != nil {
			panic(err)
		}
		strings = append(strings, ts)
	}
	return strings
}
<commit_msg>Moved dependency to legacy termui<commit_after>// tgotop project main.go

package main

import (
	"bytes"
	"fmt"
	//	spew "github.com/davecgh/go-spew/spew"
	ui "gopkg.in/gizak/termui.v1"
	//tm "github.com/nsf/termbox-go"
	"os"
	"os/signal"
	"syscall"
	"text/tabwriter"
	"time"
)

const (
	mult  = 10
	dtick = time.Second / 2 //refreshing interval
	rtick = time.Second /60 //redrawint interval
	atick = time.Second     //averaging interval
	ptick = atick / mult    //polling interval
)

func main() {
	err := ui.Init()
	if err != nil {
		panic(err)
	}
	defer ui.Close()

	qMess := ui.NewPar(":PRESS q TO QUIT")
	qMess.Height = 3

	gSwap := ui.NewGauge()
	gSwap.Height = 3

	gMem := ui.NewGauge()
	gMem.Height = 3

	gNet := ui.NewList()

	//getting ready to close stuff on command
	evt := ui.EventCh()
	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGABRT, syscall.SIGTERM)

	nd := new(netData)
	err = nd.Init(3*mult, ptick)

	if err != nil {
		panic(err)
	}

	gNet.Height = nd.size + 3
	var m memData

	go func() {
		for {
			err := m.Update()
			if err != nil {
				panic(err)
			}

			gMem.Percent = m.memPercent
			gMem.Border.Label = fillfmt("Memory", m.memUse, m.memTotal)

			gSwap.Percent = m.swapPercent
			gSwap.Border.Label = fillfmt("Swap", m.swapUse, m.swapTotal)

			gNet.Items = netf(nd)
			time.Sleep(dtick)
		}
	}()

	ui.Body.AddRows(
		ui.NewRow(
			ui.NewCol(6, 0, gMem, gSwap),
			ui.NewCol(6, 0, gNet)),
		ui.NewRow(ui.NewCol(12, 0, qMess)))

	ui.Body.Align()
	tkr := time.Tick(rtick)
	for {
		select {
		case e := <-evt:
			if dealwithevents(e) {
				return
			}
		case <-sig:
			return
		case <- tkr:
			ui.Render(ui.Body)
		}
	}
}

func fillfmt(s string, u uint64, t uint64) string {
	return fmt.Sprintf("%v used: %v / %v", s, humanBytes(float32(u)), humanBytes(float32(t)))
}

func dealwithevents(e ui.Event) bool {
	if e.Type == ui.EventKey && (e.Ch == 'q' || e.Ch == 'Q') {
		return true
	}
	if e.Type == ui.EventKey && e.Key == ui.KeyCtrlC {
		return true
	}
	if e.Type == ui.EventResize {
		ui.Body.Width = ui.TermWidth()
		ui.Body.Align()
	}
	return false
}

func netf(nd *netData) []string {
	strings := make([]string, 0, nd.size+1)
	var b bytes.Buffer
	tb := tabwriter.NewWriter(&b, 10, 8, 0, ' ', tabwriter.AlignRight)
	fmt.Fprintln(tb, "IFace\t Down\t Up\t")
	for i := 0; i < nd.size; i++ {
		fmt.Fprintf(tb, "%v:\t %s/s\t %s/s\t\n",
			nd.name[i],
			humanBytes(nd.GetD(i, mult)),
			humanBytes(nd.GetU(i, mult)))
	}

	err := tb.Flush()

	if err != nil {
		panic(err)
	}
	for i := 0; i <= nd.size; i++ {
		ts, err := b.ReadString('\n')
		if err != nil {
			panic(err)
		}
		strings = append(strings, ts)
	}
	return strings
}
<|endoftext|>


<commit_before>package handlers

import (
	"encoding/xml"
	"fmt"
	"url"

	"github.com/pilu/traffic"

	"appengine/taskqueue"
	"appengine/urlfetch"
)

/*
$ curl https://user:passwd@api.pinboard.in/v1/posts/recent

    <?xml version="1.0" encoding="UTF-8" ?>
    <posts dt="2011-03-25T14:49:56Z" user="user">
        <post href="http://www.slate.com/" description="Slate"
        extended="online news and comment"  hash="3c56b6c6cfedbe75f41e79e6fa102aba"
        tag="news opinion" time="2011-03-24T20:30:47Z" />
        ...
    </posts>
*/
type Link struct {
	XMLName xml.Name  `xml:"post"`
	Url     string    `xml:"href,attr"`
	Desc    string    `xml:"description,attr"`
	Notes   string    `xml:"extended,attr"`
	Time    time.Time `xml:"time,attr"`
	Hash    string    `xml:"hash,attr"`
	Shared  bool      `xml:"shared,attr"`
	Tags    string    `xml:"tag,attr"`
	Meta    string    `xml:"meta,attr"`
}

type Posts struct {
	XMLName xml.Name `xml:"posts"`
	Pins    []Link   `xml:"post"`
}

func LinkQueueGetHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r)
	t := taskqueue.NewPOSTTask("/link/work", url.Values{})
	_, err := taskqueue.Add(c, t, "")

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		fmt.Fprint(w, "success.\n")
	}
}

func LinkWorkGetHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r)
	user := models.GetFlagLogError(c, "PINBOARD_USER")
	token := models.GetFlagLogError(c, "PINBOARD_TOKEN")
	params := "count=100"
	pb_url = fmt.Sprintf("https://api.pinboard.in/v1/%s?auth_token=%s:%s?%s", "posts/recent", user, token, params)

	client := urlfetch.Client(c)
	resp, err := client.Get(pb_url)
	posts := new(Posts)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = xml.Unmarshal(resp, posts); err != nil {
		return nil, err
	}

	for _, pin := range posts.Pins {
		tags := strings.Fields(pin.Tags)
		e := models.NewLink(title, pin.Url, pin.Desc, tags, pin.Time)
		err = e.Save(c)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
	}
}
<commit_msg>whoops<commit_after>package handlers

import (
	"encoding/xml"
	"fmt"
	"net/url"

	"github.com/pilu/traffic"

	"appengine/taskqueue"
	"appengine/urlfetch"
)

/*
$ curl https://user:passwd@api.pinboard.in/v1/posts/recent

    <?xml version="1.0" encoding="UTF-8" ?>
    <posts dt="2011-03-25T14:49:56Z" user="user">
        <post href="http://www.slate.com/" description="Slate"
        extended="online news and comment"  hash="3c56b6c6cfedbe75f41e79e6fa102aba"
        tag="news opinion" time="2011-03-24T20:30:47Z" />
        ...
    </posts>
*/
type Link struct {
	XMLName xml.Name  `xml:"post"`
	Url     string    `xml:"href,attr"`
	Desc    string    `xml:"description,attr"`
	Notes   string    `xml:"extended,attr"`
	Time    time.Time `xml:"time,attr"`
	Hash    string    `xml:"hash,attr"`
	Shared  bool      `xml:"shared,attr"`
	Tags    string    `xml:"tag,attr"`
	Meta    string    `xml:"meta,attr"`
}

type Posts struct {
	XMLName xml.Name `xml:"posts"`
	Pins    []Link   `xml:"post"`
}

func LinkQueueGetHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r)
	t := taskqueue.NewPOSTTask("/link/work", url.Values{})
	_, err := taskqueue.Add(c, t, "")

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		fmt.Fprint(w, "success.\n")
	}
}

func LinkWorkGetHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r)
	user := models.GetFlagLogError(c, "PINBOARD_USER")
	token := models.GetFlagLogError(c, "PINBOARD_TOKEN")
	params := "count=100"
	pb_url = fmt.Sprintf("https://api.pinboard.in/v1/%s?auth_token=%s:%s?%s", "posts/recent", user, token, params)

	client := urlfetch.Client(c)
	resp, err := client.Get(pb_url)
	posts := new(Posts)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = xml.Unmarshal(resp, posts); err != nil {
		return nil, err
	}

	for _, pin := range posts.Pins {
		tags := strings.Fields(pin.Tags)
		e := models.NewLink(title, pin.Url, pin.Desc, tags, pin.Time)
		err = e.Save(c)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
	}
}
<|endoftext|>


<commit_before>package docker

import (
	"os"

	docker "github.com/fsouza/go-dockerclient"
)

type Container docker.Container

type Docker struct {
	*docker.Client
	session  string
	services map[string]*Container
}

type UpOptions struct {
	// Conf
	Cmd        []string
	Env        []string
	WorkingDir string

	// HostConf
	Binds      []string
	AutoRemove bool

	// Behavior
	Log bool
}

func New(s string) (*Docker, error) {
	client, err := docker.NewClient("unix://var/run/docker.sock")
	if err != nil {
		return nil, err
	}

	return &Docker{
		Client:   client,
		session:  s,
		services: make(map[string]*Container),
	}, nil
}

func (d *Docker) name(name string) string {
	return d.session + "_" + name
}

func (d *Docker) Up(name, image string, opts UpOptions) (string, error) {
	cName := d.name(name)

	c, err := d.findOrCreate(cName, image, &opts)
	if err != nil {
		return "", err
	}

	if !c.State.Running {
		if err := d.StartContainer(c.ID, nil); err != nil {
			return "", err
		}
	}

	if opts.Log == true {
		lOpts := docker.LogsOptions{
			Container:    c.ID,
			Follow:       true,
			OutputStream: os.Stdout,
			Stderr:       true,
			Stdout:       true,
		}
		if err := d.Logs(lOpts); err != nil {
			return "", err
		}
	}

	return cName, nil
}

func (d *Docker) findOrCreate(name, image string, opts *UpOptions) (*docker.Container, error) {
	c, err := d.InspectContainer(name)
	if err != nil {
		if _, ok := err.(*docker.NoSuchContainer); !ok {
			return nil, err
		}
	}

	// container has been found
	if c != nil {
		return c, nil
	}

	cOpts := docker.CreateContainerOptions{
		Name: name,
		Config: &docker.Config{
			Image:      image,
			Cmd:        opts.Cmd,
			Env:        opts.Env,
			WorkingDir: opts.WorkingDir,
		},
		HostConfig: &docker.HostConfig{
			AutoRemove: opts.AutoRemove,
			Binds:      opts.Binds,
		},
	}

	return d.CreateContainer(cOpts)
}

func (d *Docker) Remove(name string) error {
	return d.RemoveContainer(docker.RemoveContainerOptions{
		ID:    d.name(name),
		Force: true,
	})
}

func (d *Docker) Wait(name string) (int, error) {
	return d.Client.WaitContainer(d.name(name))
}

func (d *Docker) IP(name string) (string, error) {
	c, err := d.Client.InspectContainer(d.name(name))
	if err != nil {
		return "", err
	}

	return c.NetworkSettings.IPAddress, nil
}
<commit_msg>Log container errors too<commit_after>package docker

import (
	"os"

	docker "github.com/fsouza/go-dockerclient"
)

type Container docker.Container

type Docker struct {
	*docker.Client
	session  string
	services map[string]*Container
}

type UpOptions struct {
	// Conf
	Cmd        []string
	Env        []string
	WorkingDir string

	// HostConf
	Binds      []string
	AutoRemove bool

	// Behavior
	Log bool
}

func New(s string) (*Docker, error) {
	client, err := docker.NewClient("unix://var/run/docker.sock")
	if err != nil {
		return nil, err
	}

	return &Docker{
		Client:   client,
		session:  s,
		services: make(map[string]*Container),
	}, nil
}

func (d *Docker) name(name string) string {
	return d.session + "_" + name
}

func (d *Docker) Up(name, image string, opts UpOptions) (string, error) {
	cName := d.name(name)

	c, err := d.findOrCreate(cName, image, &opts)
	if err != nil {
		return "", err
	}

	if !c.State.Running {
		if err := d.StartContainer(c.ID, nil); err != nil {
			return "", err
		}
	}

	if opts.Log == true {
		lOpts := docker.LogsOptions{
			Container:    c.ID,
			Follow:       true,
			OutputStream: os.Stdout,
			ErrorStream:  os.Stdout,
			Stderr:       true,
			Stdout:       true,
		}
		if err := d.Logs(lOpts); err != nil {
			return "", err
		}
	}

	return cName, nil
}

func (d *Docker) findOrCreate(name, image string, opts *UpOptions) (*docker.Container, error) {
	c, err := d.InspectContainer(name)
	if err != nil {
		if _, ok := err.(*docker.NoSuchContainer); !ok {
			return nil, err
		}
	}

	// container has been found
	if c != nil {
		return c, nil
	}

	cOpts := docker.CreateContainerOptions{
		Name: name,
		Config: &docker.Config{
			Image:      image,
			Cmd:        opts.Cmd,
			Env:        opts.Env,
			WorkingDir: opts.WorkingDir,
		},
		HostConfig: &docker.HostConfig{
			AutoRemove: opts.AutoRemove,
			Binds:      opts.Binds,
		},
	}

	return d.CreateContainer(cOpts)
}

func (d *Docker) Remove(name string) error {
	return d.RemoveContainer(docker.RemoveContainerOptions{
		ID:    d.name(name),
		Force: true,
	})
}

func (d *Docker) Wait(name string) (int, error) {
	return d.Client.WaitContainer(d.name(name))
}

func (d *Docker) IP(name string) (string, error) {
	c, err := d.Client.InspectContainer(d.name(name))
	if err != nil {
		return "", err
	}

	return c.NetworkSettings.IPAddress, nil
}
<|endoftext|>


<commit_before>package pool

import (
	"io"
	"runtime"

	"github.com/DVI-GI-2017/Jira__backend/db"
)

type Job struct {
	Action Action
	Input  interface{}
}

type JobResult struct {
	WorkerId int
	Error    error
	Result   interface{}
}

var Queue = make(chan *Job, 512)
var Results = make(chan *JobResult, 512)

func InitWorkers() {
	for id := 0; id < runtime.NumCPU(); id++ {
		go worker(id, Queue, Results)
	}
}

func worker(id int, queue chan *Job, results chan<- *JobResult) {
	for job := range queue {
		function := getService(job.Action)

		result, err := function(db.Copy(), job.Input)
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			go func(job *Job, queue chan *Job) {
				queue <- job
			}(job, queue)

			continue
		}

		results <- &JobResult{
			WorkerId: id,
			Error:    err,
			Result:   result,
		}
	}
}
<commit_msg>pass origin db instead of copy.<commit_after>package pool

import (
	"io"
	"runtime"

	"github.com/DVI-GI-2017/Jira__backend/db"
)

type Job struct {
	Action Action
	Input  interface{}
}

type JobResult struct {
	WorkerId int
	Error    error
	Result   interface{}
}

var Queue = make(chan *Job, 512)
var Results = make(chan *JobResult, 512)

func InitWorkers() {
	for id := 0; id < runtime.NumCPU(); id++ {
		go worker(id, Queue, Results)
	}
}

func worker(id int, queue chan *Job, results chan<- *JobResult) {
	for job := range queue {
		function := getService(job.Action)

		result, err := function(db.Get(), job.Input)
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			go func(job *Job, queue chan *Job) {
				queue <- job
			}(job, queue)

			continue
		}

		results <- &JobResult{
			WorkerId: id,
			Error:    err,
			Result:   result,
		}
	}
}
<|endoftext|>


<commit_before>package sort

import "reflect"

func HeapSort(data interface{}, cmp func(i, j interface{}) bool) []interface{} {
	value := reflect.ValueOf(data)
	dataS := make([]interface{}, value.Len())
	for a := 0; a < value.Len(); a++ {
		dataS[a] = value.Index(a).Interface()
	}
	heapify(dataS, value.Len()/2-1, value.Len(), cmp)
	maxHeap(dataS, value.Len(), cmp)
	return dataS
}

func heapify(data []interface{}, now, last int, cmp func(i, j interface{}) bool) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapify(data, now, last, cmp)
	heapify(data, now-1, last, cmp)
	return
}

func subHeapify(data []interface{}, now, last int, cmp func(i, j interface{}) bool) {
	if (now*2+2) < last && !(cmp(data[now], data[now*2+1]) && cmp(data[now], data[now*2+2])) {
		var max int
		if cmp(data[now*2+1], data[now*2+2]) {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapify(data, max, last, cmp)
		}
	} else if ((now*2 + 1) < last) && cmp(data[now*2+1], data[now]) {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapify(data, now*2+1, last, cmp)
		}
	}
	return
}

func maxHeap(data []interface{}, len int, cmp func(i, j interface{}) bool) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapify(data, 0, len-1, cmp)
	maxHeap(data, len-1, cmp)
	return
}

// INT
func HeapSortInt(dataS []int) {
	heapifyInt(dataS, len(dataS)/2-1, len(dataS))
	maxHeapInt(dataS, len(dataS))
}

func heapifyInt(data []int, now, last int) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyInt(data, now, last)
	heapifyInt(data, now-1, last)
	return
}

func subHeapifyInt(data []int, now, last int) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyInt(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyInt(data, now*2+1, last)
		}
	}
	return
}

func maxHeapInt(data []int, len int) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyInt(data, 0, len-1)
	maxHeapInt(data, len-1)
	return
}

// INT32
func HeapSortInt32(dataS []int32) {
	heapifyInt32(dataS, len(dataS)/2-1, len(dataS))
	maxHeapInt32(dataS, len(dataS))
}

func heapifyInt32(data []int32, now, last int32) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyInt32(data, now, last)
	heapifyInt32(data, now-1, last)
	return
}

func subHeapifyInt32(data []int32, now, last int32) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int32
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyInt32(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyInt32(data, now*2+1, last)
		}
	}
	return
}

func maxHeapInt32(data []int32, len int32) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyInt32(data, 0, len-1)
	maxHeapInt32(data, len-1)
	return
}

// INT64
func HeapSortInt64(dataS []int64) {
	heapifyInt64(dataS, len(dataS)/2-1, len(dataS))
	maxHeapInt64(dataS, len(dataS))
}

func heapifyInt64(data []int64, now, last int64) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyInt64(data, now, last)
	heapifyInt64(data, now-1, last)
	return
}

func subHeapifyInt64(data []int64, now, last int64) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int64
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyInt64(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyInt64(data, now*2+1, last)
		}
	}
	return
}

func maxHeapInt64(data []int64, len int64) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyInt64(data, 0, len-1)
	maxHeapInt64(data, len-1)
	return
}

// FLOAT32
func HeapSortFloat32(dataS []float32) {
	heapifyFloat32(dataS, len(dataS)/2-1, len(dataS))
	maxHeapFloat32(dataS, len(dataS))
}

func heapifyFloat32(data []float32, now, last float32) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyFloat32(data, now, last)
	heapifyFloat32(data, now-1, last)
	return
}

func subHeapifyFloat32(data []float32, now, last float32) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max float32
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyFloat32(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyFloat32(data, now*2+1, last)
		}
	}
	return
}

func maxHeapFloat32(data []float32, len float32) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyFloat32(data, 0, len-1)
	maxHeapFloat32(data, len-1)
	return
}

// FLOAT64
func HeapSortFloat64(dataS []float64) {
	heapifyFloat64(dataS, len(dataS)/2-1, len(dataS))
	maxHeapFloat64(dataS, len(dataS))
}

func heapifyFloat64(data []float64, now, last float64) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyFloat64(data, now, last)
	heapifyFloat64(data, now-1, last)
	return
}

func subHeapifyFloat64(data []float64, now, last float64) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max float64
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyFloat64(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyFloat64(data, now*2+1, last)
		}
	}
	return
}

func maxHeapFloat64(data []float64, len float64) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyFloat64(data, 0, len-1)
	maxHeapFloat64(data, len-1)
	return
}
<commit_msg>Fix bug of different type of variable<commit_after>package sort

import (
	"reflect"
)

func HeapSort(data interface{}, cmp func(i, j interface{}) bool) []interface{} {
	value := reflect.ValueOf(data)
	dataS := make([]interface{}, value.Len())
	for a := 0; a < value.Len(); a++ {
		dataS[a] = value.Index(a).Interface()
	}
	heapify(dataS, value.Len()/2-1, value.Len(), cmp)
	maxHeap(dataS, value.Len(), cmp)
	return dataS
}

func heapify(data []interface{}, now, last int, cmp func(i, j interface{}) bool) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapify(data, now, last, cmp)
	heapify(data, now-1, last, cmp)
	return
}

func subHeapify(data []interface{}, now, last int, cmp func(i, j interface{}) bool) {
	if (now*2+2) < last && !(cmp(data[now], data[now*2+1]) && cmp(data[now], data[now*2+2])) {
		var max int
		if cmp(data[now*2+1], data[now*2+2]) {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapify(data, max, last, cmp)
		}
	} else if ((now*2 + 1) < last) && cmp(data[now*2+1], data[now]) {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapify(data, now*2+1, last, cmp)
		}
	}
	return
}

func maxHeap(data []interface{}, len int, cmp func(i, j interface{}) bool) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapify(data, 0, len-1, cmp)
	maxHeap(data, len-1, cmp)
	return
}

// INT
func HeapSortInt(dataS []int) {
	heapifyInt(dataS, len(dataS)/2-1, len(dataS))
	maxHeapInt(dataS, len(dataS))
}

func heapifyInt(data []int, now, last int) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyInt(data, now, last)
	heapifyInt(data, now-1, last)
	return
}

func subHeapifyInt(data []int, now, last int) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyInt(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyInt(data, now*2+1, last)
		}
	}
	return
}

func maxHeapInt(data []int, len int) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyInt(data, 0, len-1)
	maxHeapInt(data, len-1)
	return
}

// INT32
func HeapSortInt32(dataS []int32) {
	heapifyInt32(dataS, len(dataS)/2-1, len(dataS))
	maxHeapInt32(dataS, len(dataS))
}

func heapifyInt32(data []int32, now, last int) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyInt32(data, now, last)
	heapifyInt32(data, now-1, last)
	return
}

func subHeapifyInt32(data []int32, now, last int) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyInt32(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyInt32(data, now*2+1, last)
		}
	}
	return
}

func maxHeapInt32(data []int32, len int) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyInt32(data, 0, len-1)
	maxHeapInt32(data, len-1)
	return
}

// INT64
func HeapSortInt64(dataS []int64) {
	heapifyInt64(dataS, len(dataS)/2-1, len(dataS))
	maxHeapInt64(dataS, len(dataS))
}

func heapifyInt64(data []int64, now, last int) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyInt64(data, now, last)
	heapifyInt64(data, now-1, last)
	return
}

func subHeapifyInt64(data []int64, now, last int) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyInt64(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyInt64(data, now*2+1, last)
		}
	}
	return
}

func maxHeapInt64(data []int64, len int) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyInt64(data, 0, len-1)
	maxHeapInt64(data, len-1)
	return
}

// FLOAT32
func HeapSortFloat32(dataS []float32) {
	heapifyFloat32(dataS, len(dataS)/2-1, len(dataS))
	maxHeapFloat32(dataS, len(dataS))
}

func heapifyFloat32(data []float32, now, last int) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyFloat32(data, now, last)
	heapifyFloat32(data, now-1, last)
	return
}

func subHeapifyFloat32(data []float32, now, last int) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyFloat32(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyFloat32(data, now*2+1, last)
		}
	}
	return
}

func maxHeapFloat32(data []float32, len int) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyFloat32(data, 0, len-1)
	maxHeapFloat32(data, len-1)
	return
}

// FLOAT64
func HeapSortFloat64(dataS []float64) {
	heapifyFloat64(dataS, len(dataS)/2-1, len(dataS))
	maxHeapFloat64(dataS, len(dataS))
}

func heapifyFloat64(data []float64, now, last int) {
	if now >= last/2 || now < 0 {
		return
	}
	subHeapifyFloat64(data, now, last)
	heapifyFloat64(data, now-1, last)
	return
}

func subHeapifyFloat64(data []float64, now, last int) {
	if now*2+2 < last && !(data[now] >= data[now*2+1] && data[now] >= data[now*2+2]) {
		var max int
		if data[now*2+1] > data[now*2+2] {
			max = now*2 + 1
		} else {
			max = now*2 + 2
		}
		data[now], data[max] = data[max], data[now]
		if max < (last / 2) {
			subHeapifyFloat64(data, max, last)
		}
	} else if (now*2+1) < last && data[now] < data[now*2+1] {
		data[now], data[now*2+1] = data[now*2+1], data[now]
		if (now*2 + 1) < (last / 2) {
			subHeapifyFloat64(data, now*2+1, last)
		}
	}
	return
}

func maxHeapFloat64(data []float64, len int) {
	if len <= 1 {
		return
	}
	data[0], data[len-1] = data[len-1], data[0]
	subHeapifyFloat64(data, 0, len-1)
	maxHeapFloat64(data, len-1)
	return
}
<|endoftext|>


<commit_before>// Package ae provides tools to synchronize state between local and remote consul servers.
package ae

import (
	"errors"
	"log"
	"math"
	"sync"
	"time"

	"github.com/hashicorp/consul/lib"
)

// scaleThreshold is the number of nodes after which regular sync runs are
// spread out farther apart. The value should be a power of 2 since the
// scale function uses log2.
//
// When set to 128 nodes the delay between regular runs is doubled when the
// cluster is larger than 128 nodes. It doubles again when it passes 256
// nodes, and again at 512 nodes and so forth. At 8192 nodes, the delay
// factor is 8.
//
// If you update this, you may need to adjust the tuning of
// CoordinateUpdatePeriod and CoordinateUpdateMaxBatchSize.
const scaleThreshold = 128

// scaleFactor returns a factor by which the next sync run should be delayed to
// avoid saturation of the cluster. The larger the cluster grows the farther
// the sync runs should be spread apart.
//
// The current implementation uses a log2 scale which doubles the delay between
// runs every time the cluster doubles in size.
func scaleFactor(nodes int) int {
	if nodes <= scaleThreshold {
		return 1.0
	}
	return int(math.Ceil(math.Log2(float64(nodes))-math.Log2(float64(scaleThreshold))) + 1.0)
}

type State interface {
	SyncChanges() error
	SyncFull() error
}

// StateSyncer manages background synchronization of the given state.
//
// The state is synchronized on a regular basis or on demand when either
// the state has changed or a new Consul server has joined the cluster.
//
// The regular state sychronization provides a self-healing mechanism
// for the cluster which is also called anti-entropy.
type StateSyncer struct {
	// State contains the data that needs to be synchronized.
	State State

	// Interval is the time between two regular sync runs.
	Interval time.Duration

	// ShutdownCh is closed when the application is shutting down.
	ShutdownCh chan struct{}

	// Logger is the logger.
	Logger *log.Logger

	// ClusterSize returns the number of members in the cluster to
	// allow staggering the sync runs based on cluster size.
	// This needs to be set before Run() is called.
	ClusterSize func() int

	// SyncFull allows triggering an immediate but staggered full sync
	// in a non-blocking way.
	SyncFull *Trigger

	// SyncChanges allows triggering an immediate partial sync
	// in a non-blocking way.
	SyncChanges *Trigger

	// paused stores whether sync runs are temporarily disabled.
	pauseLock sync.Mutex
	paused    int
}

func NewStateSyner(state State, intv time.Duration, shutdownCh chan struct{}, logger *log.Logger) *StateSyncer {
	return &StateSyncer{
		State:       state,
		Interval:    intv,
		ShutdownCh:  shutdownCh,
		Logger:      logger,
		SyncFull:    NewTrigger(),
		SyncChanges: NewTrigger(),
	}
}

const (
	// serverUpIntv is the max time to wait before a sync is triggered
	// when a consul server has been added to the cluster.
	serverUpIntv = 3 * time.Second

	// retryFailIntv is the min time to wait before a failed sync is retried.
	retryFailIntv = 15 * time.Second
)

var errPaused = errors.New("paused")

// Run is the long running method to perform state synchronization
// between local and remote servers.
func (s *StateSyncer) Run() {
	if s.ClusterSize == nil {
		panic("ClusterSize not set")
	}

	stagger := func(d time.Duration) time.Duration {
		f := scaleFactor(s.ClusterSize())
		return lib.RandomStagger(time.Duration(f) * d)
	}

FullSync:
	for {
		// attempt a full sync
		err := s.ifNotPausedRun(s.State.SyncFull)
		if err != nil {
			if err != errPaused {
				s.Logger.Printf("[ERR] agent: failed to sync remote state: %v", err)
			}

			select {
			// trigger a full sync immediately.
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
				case <-s.ShutdownCh:
					return
				}

			// retry full sync after some time
			// todo(fs): why don't we use s.Interval here?
			case <-time.After(retryFailIntv + stagger(retryFailIntv)):

			case <-s.ShutdownCh:
				return
			}

			continue
		}

		// do partial syncs until it is time for a full sync again
		for {
			select {
			// trigger a full sync immediately
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
					continue FullSync
				case <-s.ShutdownCh:
					return
				}

			// time for a full sync again
			case <-time.After(s.Interval + stagger(s.Interval)):
				continue FullSync

			// do partial syncs on demand
			case <-s.SyncChanges.Notif():
				err := s.ifNotPausedRun(s.State.SyncChanges)
				if err != nil && err != errPaused {
					s.Logger.Printf("[ERR] agent: failed to sync changes: %v", err)
				}

			case <-s.ShutdownCh:
				return
			}
		}
	}
}

func (s *StateSyncer) ifNotPausedRun(f func() error) error {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	if s.paused != 0 {
		return errPaused
	}
	return f()
}

// Pause temporarily disables sync runs.
func (s *StateSyncer) Pause() {
	s.pauseLock.Lock()
	s.paused++
	s.pauseLock.Unlock()
}

// Paused returns whether sync runs are temporarily disabled.
func (s *StateSyncer) Paused() bool {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	return s.paused != 0
}

// Resume re-enables sync runs.
func (s *StateSyncer) Resume() {
	s.pauseLock.Lock()
	s.paused--
	if s.paused < 0 {
		panic("unbalanced pause/resume")
	}
	if s.paused == 0 {
		s.SyncChanges.Trigger()
	}
	s.pauseLock.Unlock()
}
<commit_msg>Revert "ae: restore previous pause/resume behavior"<commit_after>// Package ae provides tools to synchronize state between local and remote consul servers.
package ae

import (
	"errors"
	"log"
	"math"
	"sync"
	"time"

	"github.com/hashicorp/consul/lib"
)

// scaleThreshold is the number of nodes after which regular sync runs are
// spread out farther apart. The value should be a power of 2 since the
// scale function uses log2.
//
// When set to 128 nodes the delay between regular runs is doubled when the
// cluster is larger than 128 nodes. It doubles again when it passes 256
// nodes, and again at 512 nodes and so forth. At 8192 nodes, the delay
// factor is 8.
//
// If you update this, you may need to adjust the tuning of
// CoordinateUpdatePeriod and CoordinateUpdateMaxBatchSize.
const scaleThreshold = 128

// scaleFactor returns a factor by which the next sync run should be delayed to
// avoid saturation of the cluster. The larger the cluster grows the farther
// the sync runs should be spread apart.
//
// The current implementation uses a log2 scale which doubles the delay between
// runs every time the cluster doubles in size.
func scaleFactor(nodes int) int {
	if nodes <= scaleThreshold {
		return 1.0
	}
	return int(math.Ceil(math.Log2(float64(nodes))-math.Log2(float64(scaleThreshold))) + 1.0)
}

type State interface {
	SyncChanges() error
	SyncFull() error
}

// StateSyncer manages background synchronization of the given state.
//
// The state is synchronized on a regular basis or on demand when either
// the state has changed or a new Consul server has joined the cluster.
//
// The regular state sychronization provides a self-healing mechanism
// for the cluster which is also called anti-entropy.
type StateSyncer struct {
	// State contains the data that needs to be synchronized.
	State State

	// Interval is the time between two regular sync runs.
	Interval time.Duration

	// ShutdownCh is closed when the application is shutting down.
	ShutdownCh chan struct{}

	// Logger is the logger.
	Logger *log.Logger

	// ClusterSize returns the number of members in the cluster to
	// allow staggering the sync runs based on cluster size.
	// This needs to be set before Run() is called.
	ClusterSize func() int

	// SyncFull allows triggering an immediate but staggered full sync
	// in a non-blocking way.
	SyncFull *Trigger

	// SyncChanges allows triggering an immediate partial sync
	// in a non-blocking way.
	SyncChanges *Trigger

	// paused stores whether sync runs are temporarily disabled.
	pauseLock sync.Mutex
	paused    bool
}

func NewStateSyner(state State, intv time.Duration, shutdownCh chan struct{}, logger *log.Logger) *StateSyncer {
	return &StateSyncer{
		State:       state,
		Interval:    intv,
		ShutdownCh:  shutdownCh,
		Logger:      logger,
		SyncFull:    NewTrigger(),
		SyncChanges: NewTrigger(),
	}
}

const (
	// serverUpIntv is the max time to wait before a sync is triggered
	// when a consul server has been added to the cluster.
	serverUpIntv = 3 * time.Second

	// retryFailIntv is the min time to wait before a failed sync is retried.
	retryFailIntv = 15 * time.Second
)

var errPaused = errors.New("paused")

// Run is the long running method to perform state synchronization
// between local and remote servers.
func (s *StateSyncer) Run() {
	if s.ClusterSize == nil {
		panic("ClusterSize not set")
	}

	stagger := func(d time.Duration) time.Duration {
		f := scaleFactor(s.ClusterSize())
		return lib.RandomStagger(time.Duration(f) * d)
	}

FullSync:
	for {
		// attempt a full sync
		err := s.ifNotPausedRun(s.State.SyncFull)
		if err != nil {
			if err != errPaused {
				s.Logger.Printf("[ERR] agent: failed to sync remote state: %v", err)
			}

			select {
			// trigger a full sync immediately.
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
				case <-s.ShutdownCh:
					return
				}

			// retry full sync after some time
			// todo(fs): why don't we use s.Interval here?
			case <-time.After(retryFailIntv + stagger(retryFailIntv)):

			case <-s.ShutdownCh:
				return
			}

			continue
		}

		// do partial syncs until it is time for a full sync again
		for {
			select {
			// trigger a full sync immediately
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
					continue FullSync
				case <-s.ShutdownCh:
					return
				}

			// time for a full sync again
			case <-time.After(s.Interval + stagger(s.Interval)):
				continue FullSync

			// do partial syncs on demand
			case <-s.SyncChanges.Notif():
				err := s.ifNotPausedRun(s.State.SyncChanges)
				if err != nil && err != errPaused {
					s.Logger.Printf("[ERR] agent: failed to sync changes: %v", err)
				}

			case <-s.ShutdownCh:
				return
			}
		}
	}
}

func (s *StateSyncer) ifNotPausedRun(f func() error) error {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	if s.paused {
		return errPaused
	}
	return f()
}

// Pause temporarily disables sync runs.
func (s *StateSyncer) Pause() {
	s.pauseLock.Lock()
	if s.paused {
		panic("pause while paused")
	}
	s.paused = true
	s.pauseLock.Unlock()
}

// Paused returns whether sync runs are temporarily disabled.
func (s *StateSyncer) Paused() bool {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	return s.paused
}

// Resume re-enables sync runs.
func (s *StateSyncer) Resume() {
	s.pauseLock.Lock()
	if !s.paused {
		panic("resume while not paused")
	}
	s.paused = false
	s.pauseLock.Unlock()
	s.SyncChanges.Trigger()
}
<|endoftext|>


<commit_before><commit_msg>Fix compile error.<commit_after><|endoftext|>


<commit_before>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import (
	"bytes"
	"compress/gzip"
	"crypto/sha512"
	"database/sql"
	"encoding/base64"
	"fmt"
	"net/http"
	"strings"
	"time"
	"unicode"

	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
	"github.com/apache/incubator-trafficcontrol/traffic_ops/tocookie"
)

const ServerName = "traffic_ops_golang" + "/" + Version

type AuthRegexHandlerFunc func(w http.ResponseWriter, r *http.Request, params PathParams, user string, privLevel int)

func wrapHeaders(h RegexHandlerFunc) RegexHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, p PathParams) {
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie")
		w.Header().Set("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("X-Server-Name", ServerName)
		iw := &BodyInterceptor{w: w}
		h(iw, r, p)

		sha := sha512.Sum512(iw.Body())
		w.Header().Set("Whole-Content-SHA512", base64.StdEncoding.EncodeToString(sha[:]))

		if acceptsGzip(r) {
			gzipResponse(w, r, iw.Body())
		} else {
			iw.RealWrite(iw.Body())
		}

	}
}

func handlerToAuthHandler(h RegexHandlerFunc) AuthRegexHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, p PathParams, user string, privLevel int) { h(w, r, p) }
}

func wrapAuth(h RegexHandlerFunc, noAuth bool, secret string, privLevelStmt *sql.Stmt, privLevelRequired int) RegexHandlerFunc {
	return wrapAuthWithData(handlerToAuthHandler(h), noAuth, secret, privLevelStmt, privLevelRequired)
}

func wrapAuthWithData(h AuthRegexHandlerFunc, noAuth bool, secret string, privLevelStmt *sql.Stmt, privLevelRequired int) RegexHandlerFunc {
	if noAuth {
		return func(w http.ResponseWriter, r *http.Request, p PathParams) {
			h(w, r, p, "", PrivLevelInvalid)
		}
	}
	return func(w http.ResponseWriter, r *http.Request, p PathParams) {
		// TODO remove, and make username available to wrapLogTime
		start := time.Now()
		iw := &Interceptor{w: w}
		w = iw
		username := "-"
		defer func() {
			log.EventfRaw(`%s - %s [%s] "%v %v HTTP/1.1" %v %v %v "%v"`, r.RemoteAddr, username, time.Now().Format(AccessLogTimeFormat), r.Method, r.URL.Path, iw.code, iw.byteCount, int(time.Now().Sub(start)/time.Millisecond), r.UserAgent())
		}()

		handleUnauthorized := func(reason string) {
			status := http.StatusUnauthorized
			w.WriteHeader(status)
			fmt.Fprintf(w, http.StatusText(status))
			log.Infof("%v %v %v %v returned unauthorized: %v\n", r.RemoteAddr, r.Method, r.URL.Path, username, reason)
		}

		cookie, err := r.Cookie(tocookie.Name)
		if err != nil {
			handleUnauthorized("error getting cookie: " + err.Error())
			return
		}

		if cookie == nil {
			handleUnauthorized("no auth cookie")
			return
		}

		oldCookie, err := tocookie.Parse(secret, cookie.Value)
		if err != nil {
			handleUnauthorized("cookie error: " + err.Error())
			return
		}

		username = oldCookie.AuthData
		privLevel := PrivLevel(privLevelStmt, username)
		if privLevel < privLevelRequired {
			handleUnauthorized("insufficient privileges")
			return
		}

		newCookieVal := tocookie.Refresh(oldCookie, secret)
		http.SetCookie(w, &http.Cookie{Name: tocookie.Name, Value: newCookieVal, Path: "/", HttpOnly: true})

		h(w, r, p, username, privLevel)
	}
}

const AccessLogTimeFormat = "02/Jan/2006:15:04:05 -0700"

func wrapAccessLog(secret string, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		iw := &Interceptor{w: w}
		user := "-"
		cookie, err := r.Cookie(tocookie.Name)
		if err == nil && cookie != nil {
			cookie, err := tocookie.Parse(secret, cookie.Value)
			if err == nil {
				user = cookie.AuthData
			}
		}
		start := time.Now()
		defer func() {
			log.EventfRaw(`%s - %s [%s] "%v %v HTTP/1.1" %v %v %v "%v"`, r.RemoteAddr, user, time.Now().Format(AccessLogTimeFormat), r.Method, r.URL.Path, iw.code, iw.byteCount, int(time.Now().Sub(start)/time.Millisecond), r.UserAgent())
		}()
		h.ServeHTTP(iw, r)
	}
}

// gzipResponse takes a function which cannot error and returns only bytes, and wraps it as a http.HandlerFunc. The errContext is logged if the write fails, and should be enough information to trace the problem (function name, endpoint, request parameters, etc).
func gzipResponse(w http.ResponseWriter, r *http.Request, bytes []byte) {

	bytes, err := gzipIfAccepts(r, w, bytes)
	if err != nil {
		log.Errorf("gzipping request '%v': %v\n", r.URL.EscapedPath(), err)
		code := http.StatusInternalServerError
		w.WriteHeader(code)
		if _, err := w.Write([]byte(http.StatusText(code))); err != nil {
			log.Warnf("received error writing data request %v: %v\n", r.URL.EscapedPath(), err)
		}
		return
	}

	w.Write(bytes)
}

// wrapBytes takes a function which cannot error and returns only bytes, and wraps it as a http.HandlerFunc. The errContext is logged if the write fails, and should be enough information to trace the problem (function name, endpoint, request parameters, etc).
//TODO: drichardson - refactor these to a generic area
func wrapBytes(f func() []byte, contentType string) RegexHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, p PathParams) {
		bytes := f()
		bytes, err := gzipIfAccepts(r, w, bytes)
		if err != nil {
			log.Errorf("gzipping request '%v': %v\n", r.URL.EscapedPath(), err)
			code := http.StatusInternalServerError
			w.WriteHeader(code)
			if _, err := w.Write([]byte(http.StatusText(code))); err != nil {
				log.Warnf("received error writing data request %v: %v\n", r.URL.EscapedPath(), err)
			}
			return
		}

		w.Header().Set("Content-Type", contentType)
		log.Write(w, bytes, r.URL.EscapedPath())
	}
}

// gzipIfAccepts gzips the given bytes, writes a `Content-Encoding: gzip` header to the given writer, and returns the gzipped bytes, if the Request supports GZip (has an Accept-Encoding header). Else, returns the bytes unmodified. Note the given bytes are NOT written to the given writer. It is assumed the bytes may need to pass thru other middleware before being written.
//TODO: drichardson - refactor these to a generic area
func gzipIfAccepts(r *http.Request, w http.ResponseWriter, b []byte) ([]byte, error) {
	// TODO this could be made more efficient by wrapping ResponseWriter with the GzipWriter, and letting callers writer directly to it - but then we'd have to deal with Closing the gzip.Writer.
	if len(b) == 0 {
		return b, nil
	}
	w.Header().Set("Content-Encoding", "gzip")

	buf := bytes.Buffer{}
	zw := gzip.NewWriter(&buf)

	if _, err := zw.Write(b); err != nil {
		return nil, fmt.Errorf("gzipping bytes: %v")
	}

	if err := zw.Close(); err != nil {
		return nil, fmt.Errorf("closing gzip writer: %v")
	}

	return buf.Bytes(), nil
}

func acceptsGzip(r *http.Request) bool {
	encodingHeaders := r.Header["Accept-Encoding"] // headers are case-insensitive, but Go promises to Canonical-Case requests
	for _, encodingHeader := range encodingHeaders {
		encodingHeader = stripAllWhitespace(encodingHeader)
		encodings := strings.Split(encodingHeader, ",")
		for _, encoding := range encodings {
			if strings.ToLower(encoding) == "gzip" { // encoding is case-insensitive, per the RFC
				return true
			}
		}
	}
	return false
}

func stripAllWhitespace(s string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, s)
}

type Interceptor struct {
	w         http.ResponseWriter
	code      int
	byteCount int
}

func (i *Interceptor) WriteHeader(rc int) {
	i.w.WriteHeader(rc)
	i.code = rc
}

func (i *Interceptor) Write(b []byte) (int, error) {
	wi, werr := i.w.Write(b)
	i.byteCount += wi
	if i.code == 0 {
		i.code = 200
	}
	return wi, werr
}

func (i *Interceptor) Header() http.Header {
	return i.w.Header()
}

// BodyInterceptor fulfills the Writer interface, but records the body and doesn't actually write. This allows performing operations on the entire body written by a handler, for example, compressing or hashing. To actually write, call `RealWrite()`. Note this means `len(b)` and `nil` are always returned by `Write()`, any real write errors will be returned by `RealWrite()`.
type BodyInterceptor struct {
	w    http.ResponseWriter
	body []byte
}

func (i *BodyInterceptor) WriteHeader(rc int) {
	i.w.WriteHeader(rc)
}
func (i *BodyInterceptor) Write(b []byte) (int, error) {
	i.body = append(i.body, b...)
	return len(b), nil
}
func (i *BodyInterceptor) Header() http.Header {
	return i.w.Header()
}
func (i *BodyInterceptor) RealWrite(b []byte) (int, error) {
	wi, werr := i.w.Write(i.body)
	return wi, werr
}
func (i *BodyInterceptor) Body() []byte {
	return i.body
}
<commit_msg>refactored to remove the if-else RealWrite<commit_after>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import (
	"bytes"
	"compress/gzip"
	"crypto/sha512"
	"database/sql"
	"encoding/base64"
	"fmt"
	"net/http"
	"strings"
	"time"
	"unicode"

	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
	"github.com/apache/incubator-trafficcontrol/traffic_ops/tocookie"
)

const ServerName = "traffic_ops_golang" + "/" + Version

type AuthRegexHandlerFunc func(w http.ResponseWriter, r *http.Request, params PathParams, user string, privLevel int)

func wrapHeaders(h RegexHandlerFunc) RegexHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, p PathParams) {
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie")
		w.Header().Set("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("X-Server-Name", ServerName)
		iw := &BodyInterceptor{w: w}
		h(iw, r, p)

		sha := sha512.Sum512(iw.Body())
		w.Header().Set("Whole-Content-SHA512", base64.StdEncoding.EncodeToString(sha[:]))

		gzipResponse(w, r, iw.Body())

	}
}

func handlerToAuthHandler(h RegexHandlerFunc) AuthRegexHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, p PathParams, user string, privLevel int) { h(w, r, p) }
}

func wrapAuth(h RegexHandlerFunc, noAuth bool, secret string, privLevelStmt *sql.Stmt, privLevelRequired int) RegexHandlerFunc {
	return wrapAuthWithData(handlerToAuthHandler(h), noAuth, secret, privLevelStmt, privLevelRequired)
}

func wrapAuthWithData(h AuthRegexHandlerFunc, noAuth bool, secret string, privLevelStmt *sql.Stmt, privLevelRequired int) RegexHandlerFunc {
	if noAuth {
		return func(w http.ResponseWriter, r *http.Request, p PathParams) {
			h(w, r, p, "", PrivLevelInvalid)
		}
	}
	return func(w http.ResponseWriter, r *http.Request, p PathParams) {
		// TODO remove, and make username available to wrapLogTime
		start := time.Now()
		iw := &Interceptor{w: w}
		w = iw
		username := "-"
		defer func() {
			log.EventfRaw(`%s - %s [%s] "%v %v HTTP/1.1" %v %v %v "%v"`, r.RemoteAddr, username, time.Now().Format(AccessLogTimeFormat), r.Method, r.URL.Path, iw.code, iw.byteCount, int(time.Now().Sub(start)/time.Millisecond), r.UserAgent())
		}()

		handleUnauthorized := func(reason string) {
			status := http.StatusUnauthorized
			w.WriteHeader(status)
			fmt.Fprintf(w, http.StatusText(status))
			log.Infof("%v %v %v %v returned unauthorized: %v\n", r.RemoteAddr, r.Method, r.URL.Path, username, reason)
		}

		cookie, err := r.Cookie(tocookie.Name)
		if err != nil {
			handleUnauthorized("error getting cookie: " + err.Error())
			return
		}

		if cookie == nil {
			handleUnauthorized("no auth cookie")
			return
		}

		oldCookie, err := tocookie.Parse(secret, cookie.Value)
		if err != nil {
			handleUnauthorized("cookie error: " + err.Error())
			return
		}

		username = oldCookie.AuthData
		privLevel := PrivLevel(privLevelStmt, username)
		if privLevel < privLevelRequired {
			handleUnauthorized("insufficient privileges")
			return
		}

		newCookieVal := tocookie.Refresh(oldCookie, secret)
		http.SetCookie(w, &http.Cookie{Name: tocookie.Name, Value: newCookieVal, Path: "/", HttpOnly: true})

		h(w, r, p, username, privLevel)
	}
}

const AccessLogTimeFormat = "02/Jan/2006:15:04:05 -0700"

func wrapAccessLog(secret string, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		iw := &Interceptor{w: w}
		user := "-"
		cookie, err := r.Cookie(tocookie.Name)
		if err == nil && cookie != nil {
			cookie, err := tocookie.Parse(secret, cookie.Value)
			if err == nil {
				user = cookie.AuthData
			}
		}
		start := time.Now()
		defer func() {
			log.EventfRaw(`%s - %s [%s] "%v %v HTTP/1.1" %v %v %v "%v"`, r.RemoteAddr, user, time.Now().Format(AccessLogTimeFormat), r.Method, r.URL.Path, iw.code, iw.byteCount, int(time.Now().Sub(start)/time.Millisecond), r.UserAgent())
		}()
		h.ServeHTTP(iw, r)
	}
}

// gzipResponse takes a function which cannot error and returns only bytes, and wraps it as a http.HandlerFunc. The errContext is logged if the write fails, and should be enough information to trace the problem (function name, endpoint, request parameters, etc).
func gzipResponse(w http.ResponseWriter, r *http.Request, bytes []byte) {

	bytes, err := gzipIfAccepts(r, w, bytes)
	if err != nil {
		log.Errorf("gzipping request '%v': %v\n", r.URL.EscapedPath(), err)
		code := http.StatusInternalServerError
		w.WriteHeader(code)
		if _, err := w.Write([]byte(http.StatusText(code))); err != nil {
			log.Warnf("received error writing data request %v: %v\n", r.URL.EscapedPath(), err)
		}
		return
	}

	w.Write(bytes)
}

// wrapBytes takes a function which cannot error and returns only bytes, and wraps it as a http.HandlerFunc. The errContext is logged if the write fails, and should be enough information to trace the problem (function name, endpoint, request parameters, etc).
//TODO: drichardson - refactor these to a generic area
func wrapBytes(f func() []byte, contentType string) RegexHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, p PathParams) {
		bytes := f()
		bytes, err := gzipIfAccepts(r, w, bytes)
		if err != nil {
			log.Errorf("gzipping request '%v': %v\n", r.URL.EscapedPath(), err)
			code := http.StatusInternalServerError
			w.WriteHeader(code)
			if _, err := w.Write([]byte(http.StatusText(code))); err != nil {
				log.Warnf("received error writing data request %v: %v\n", r.URL.EscapedPath(), err)
			}
			return
		}

		w.Header().Set("Content-Type", contentType)
		log.Write(w, bytes, r.URL.EscapedPath())
	}
}

// gzipIfAccepts gzips the given bytes, writes a `Content-Encoding: gzip` header to the given writer, and returns the gzipped bytes, if the Request supports GZip (has an Accept-Encoding header). Else, returns the bytes unmodified. Note the given bytes are NOT written to the given writer. It is assumed the bytes may need to pass thru other middleware before being written.
//TODO: drichardson - refactor these to a generic area
func gzipIfAccepts(r *http.Request, w http.ResponseWriter, b []byte) ([]byte, error) {
	// TODO this could be made more efficient by wrapping ResponseWriter with the GzipWriter, and letting callers writer directly to it - but then we'd have to deal with Closing the gzip.Writer.
	if len(b) == 0 || !acceptsGzip(r) {
		return b, nil
	}
	w.Header().Set("Content-Encoding", "gzip")

	buf := bytes.Buffer{}
	zw := gzip.NewWriter(&buf)

	if _, err := zw.Write(b); err != nil {
		return nil, fmt.Errorf("gzipping bytes: %v")
	}

	if err := zw.Close(); err != nil {
		return nil, fmt.Errorf("closing gzip writer: %v")
	}

	return buf.Bytes(), nil
}

func acceptsGzip(r *http.Request) bool {
	encodingHeaders := r.Header["Accept-Encoding"] // headers are case-insensitive, but Go promises to Canonical-Case requests
	for _, encodingHeader := range encodingHeaders {
		encodingHeader = stripAllWhitespace(encodingHeader)
		encodings := strings.Split(encodingHeader, ",")
		for _, encoding := range encodings {
			if strings.ToLower(encoding) == "gzip" { // encoding is case-insensitive, per the RFC
				return true
			}
		}
	}
	return false
}

func stripAllWhitespace(s string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, s)
}

type Interceptor struct {
	w         http.ResponseWriter
	code      int
	byteCount int
}

func (i *Interceptor) WriteHeader(rc int) {
	i.w.WriteHeader(rc)
	i.code = rc
}

func (i *Interceptor) Write(b []byte) (int, error) {
	wi, werr := i.w.Write(b)
	i.byteCount += wi
	if i.code == 0 {
		i.code = 200
	}
	return wi, werr
}

func (i *Interceptor) Header() http.Header {
	return i.w.Header()
}

// BodyInterceptor fulfills the Writer interface, but records the body and doesn't actually write. This allows performing operations on the entire body written by a handler, for example, compressing or hashing. To actually write, call `RealWrite()`. Note this means `len(b)` and `nil` are always returned by `Write()`, any real write errors will be returned by `RealWrite()`.
type BodyInterceptor struct {
	w    http.ResponseWriter
	body []byte
}

func (i *BodyInterceptor) WriteHeader(rc int) {
	i.w.WriteHeader(rc)
}
func (i *BodyInterceptor) Write(b []byte) (int, error) {
	i.body = append(i.body, b...)
	return len(b), nil
}
func (i *BodyInterceptor) Header() http.Header {
	return i.w.Header()
}
func (i *BodyInterceptor) RealWrite(b []byte) (int, error) {
	wi, werr := i.w.Write(i.body)
	return wi, werr
}
func (i *BodyInterceptor) Body() []byte {
	return i.body
}
<|endoftext|>


<commit_before>package handlers

import (
	"net/http"
	"strconv"

	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/user"

	"github.com/icco/natnatnat/models"
	"github.com/pilu/traffic"
)

type RootData struct {
	Posts   interface{}
	IsAdmin bool
	Page    int64
}

const perPage = 50

func RootHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)
	pg, err := strconv.ParseInt(r.Param("page"), 10, 64)
	if err != nil {
		log.Infof(c, "Error parsing: %+v")
		pg = 0
	}

	entries, err := models.Pagination(c, perPage, int(pg*perPage))
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	data := &RootData{Posts: entries, IsAdmin: user.IsAdmin(c), Page: pg}
	w.Render("index", data)
}

func AboutHandler(w traffic.ResponseWriter, r *traffic.Request) {
	http.Redirect(w, r.Request, "http://natwelch.com", 301)
}

func UnimplementedHandler(w traffic.ResponseWriter, r *traffic.Request) {
	http.Error(w, "Sorry, I haven't implemented this yet", 500)
}

func MarkdownHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)

	err := r.ParseForm()
	if err != nil {
		log.Warningf(c, "Couldn't parse form: %v", r)
		http.Error(w, "Unable to parse request.", 500)
		return
	}

	in := r.Request.FormValue("text")
	md := models.Markdown(in)

	log.Infof(c, "Markdown Recieved: %s", in)
	log.Infof(c, "Markdown Rendered: %s", md)
	w.WriteText(string(md))
}
<commit_msg>better loggin<commit_after>package handlers

import (
	"net/http"
	"strconv"

	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/user"

	"github.com/icco/natnatnat/models"
	"github.com/pilu/traffic"
)

type RootData struct {
	Posts   interface{}
	IsAdmin bool
	Page    int64
}

const perPage = 50

func RootHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)
	pg, err := strconv.ParseInt(r.Param("page"), 10, 64)
	if err != nil {
		log.Infof(c, "Error parsing: %+v", err)
		pg = 0
	}

	entries, err := models.Pagination(c, perPage, int(pg*perPage))
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	data := &RootData{Posts: entries, IsAdmin: user.IsAdmin(c), Page: pg}
	w.Render("index", data)
}

func AboutHandler(w traffic.ResponseWriter, r *traffic.Request) {
	http.Redirect(w, r.Request, "http://natwelch.com", 301)
}

func UnimplementedHandler(w traffic.ResponseWriter, r *traffic.Request) {
	http.Error(w, "Sorry, I haven't implemented this yet", 500)
}

func MarkdownHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)

	err := r.ParseForm()
	if err != nil {
		log.Warningf(c, "Couldn't parse form: %v", r)
		http.Error(w, "Unable to parse request.", 500)
		return
	}

	in := r.Request.FormValue("text")
	md := models.Markdown(in)

	log.Infof(c, "Markdown Recieved: %s", in)
	log.Infof(c, "Markdown Rendered: %s", md)
	w.WriteText(string(md))
}
<|endoftext|>


<commit_before>package docker

import (
    "fmt"
    "github.com/fsouza/go-dockerclient"
    "log"
    "net"
    "path"
    "strconv"
)

type DockerConfig struct {
    Endpoint string
}

type Docker struct {
    config DockerConfig
    client *docker.Client

    // convenience info from docker
    Version string

    // XXX: not supported on docker 1.3.3
    Name string
}

type Container struct {
    // local unique ID for continer
    ID          string

    // optional human-readble name for container, or ID
    Name        string

    // Current running state
    Running     bool

    // internal IPv4 address assigned to container
    IPv4        net.IP

    // internal hostname for container, or short ID
    Hostname    string

    // basename of image used to run container
    Image       string

    // exposed, published ports
    Ports       []Port

    // XXX: configured image, run labels?
    Labels      map[string]string
}

type Port struct {
    Proto       string
    Port        uint16

    // exposed
    HostIP      string
    HostPort    uint16
}

type ContainerEvent struct {
    ID          string
    Status      string

    // Interpretation of running state after this event
    Running     bool

    // Current state of container; may be inconsistent or missing
    State       *Container
}

func (self DockerConfig) Open() (*Docker, error) {
    d := &Docker{config: self}

    if err := d.open(); err != nil {
        return nil, err
    } else {
        return d, err
    }
}

func (self *Docker) open() error {
    var dockerClient *docker.Client
    var err error

    if self.config.Endpoint != "" {
        dockerClient, err = docker.NewClient(self.config.Endpoint)
    } else {
        dockerClient, err = docker.NewClientFromEnv()
    }

    if err != nil {
        return err
    } else {
        self.client = dockerClient
    }

    // Version
    if env, err := self.client.Version(); err != nil {
        return err
    } else {
        self.Version = env.Get("Version")
    }

    // Info
    if env, err := self.client.Info(); err != nil {
        return err
    } else {
        self.Name = env.Get("Name")
    }

    return nil
}

func (self *Docker) String() string {
    return fmt.Sprintf("Docker<%v>", self.config)
}

func parsePort(portString string) (uint16, error) {
    if portValue, err := strconv.Atoi(portString); err != nil {
        return 0, fmt.Errorf("port invalid: %v", portString)
    } else if portValue <= 0 || portValue >= (1 << 16) {
        return 0, fmt.Errorf("port out of range: %v", portString)
    } else {
        return uint16(portValue), nil
    }
}

/*
 * Return the state of the given container, based on the given event.
 *
 *  event       - /event status, or "" when listing
 */
func (self *Docker) inspectContainer(id string) (*Container, error) {
    dockerContainer, err := self.client.InspectContainer(id)
    if err != nil {
        log.Printf("%v.inspectContainer(%v): %v\n", self, id, err)
        return nil, err
    }

    state := Container{
        ID:         id,
        Name:       path.Base(dockerContainer.Name),
        Running:    dockerContainer.State.Running,
        IPv4:       net.ParseIP(dockerContainer.NetworkSettings.IPAddress),
        Hostname:   dockerContainer.Config.Hostname,
        Image:      path.Base(dockerContainer.Config.Image),
        Labels:     dockerContainer.Config.Labels,
    }

    for dockerPort, portBindings := range dockerContainer.NetworkSettings.Ports {
        port := Port{
            Proto:  dockerPort.Proto(),
        }

        if portValue, err := parsePort(dockerPort.Port()); err != nil {
            return nil, err
        } else {
            port.Port = portValue
        }

        for _, portBinding := range portBindings {
            // XXX: choose one
            port.HostIP = portBinding.HostIP

            if hostPort, err := parsePort(portBinding.HostPort); err != nil {
                return nil, err
            } else {
                port.HostPort = hostPort
            }
        }

        state.Ports = append(state.Ports, port)
    }

    return &state, nil
}

/*
 * Full list of (running) containers.
 *
 * TODO: somehow synchronize this with Subscribe() events to ensure consistency during listings?
 */
func (self *Docker) List() (out []*Container, err error) {
    containers, err := self.client.ListContainers(docker.ListContainersOptions{All: true})
    if err != nil {
        log.Printf("%v.ListContainers: %v\n", self, err)
        return nil, err
    }

    for _, listContainer := range containers {
        if containerState, err := self.inspectContainer(listContainer.ID); err != nil {
            break
        } else {
            out = append(out, containerState)
        }
    }

    return out, nil
}

// Handle a container event
func (self *Docker) containerEvent(dockerEvent *docker.APIEvents) (event ContainerEvent, err error) {
    event.ID = dockerEvent.ID
    event.Status = dockerEvent.Status

    if containerState, err := self.inspectContainer(dockerEvent.ID); err != nil {
        // skip lookup for cases where we don't have the container state anymore
        // this is normal for "destroy", but other events could also race
        event.State = nil

        // XXX: Running is indeterminite, but we can assume it is not?

    } else {
        event.Running = containerState.Running
        event.State = containerState
    }

    if dockerEvent.Status == "die" {
        // XXX: docker seems to be inconsistent about the inspected container State.Running=true/false immediately after a die?
        event.Running = false
    }

    return
}

/*
 * Subscribe to container events.
 */
func (self *Docker) Subscribe() (chan ContainerEvent, error) {
    listener := make(chan *docker.APIEvents)
    out := make(chan ContainerEvent)

    if err := self.client.AddEventListener(listener); err != nil {
        log.Printf("%v.Subscribe: AddEventListener\n", self, err)
        return nil, err
    }

    go func() {
        defer close(out)

        for dockerEvent := range listener {
            switch dockerEvent.Status {
            case "EOF":
                // XXX: how is this different to close()'ing the chan?
                log.Printf("%v.Subscribe: EOF\n", self)
                break

            // container events
            case "attach", "commit", "copy", "create", "destroy", "die", "exec_create", "exec_start", "export", "kill", "oom", "pause", "rename", "resize", "restart", "start", "stop", "top", "unpause":
                if containerEvent, err := self.containerEvent(dockerEvent); err != nil {
                    log.Printf("%v.Subscribe %v:%v: containerEvent: %v\n", self, dockerEvent.Status, dockerEvent.ID, err)

                } else {
                    // log.Printf("%v.Subscribe %v:%v: %#v\n", self, dockerEvent.Status, dockerEvent.ID, containerEvent)

                    out <- containerEvent
                }

            // image events
            case "delete", "import", "pull", "push", "tag", "untag":
                log.Printf("%v.Subscribe %v:%v: image event: ignore\n", self, dockerEvent.Status, dockerEvent.ID)

            default:
                log.Printf("%v.Subscribe %v:%v: unknown event: ignore\n", self, dockerEvent.Status, dockerEvent.ID)
            }
        }
    }()

    return out, nil
}
<commit_msg>docker: provide String() for Container, ContainerEvent<commit_after>package docker

import (
    "fmt"
    "github.com/fsouza/go-dockerclient"
    "log"
    "net"
    "path"
    "strconv"
)

type DockerConfig struct {
    Endpoint string
}

type Docker struct {
    config DockerConfig
    client *docker.Client

    // convenience info from docker
    Version string

    // XXX: not supported on docker 1.3.3
    Name string
}

type Container struct {
    // local unique ID for continer
    ID          string

    // optional human-readble name for container, or ID
    Name        string

    // Current running state
    Running     bool

    // internal IPv4 address assigned to container
    IPv4        net.IP

    // internal hostname for container, or short ID
    Hostname    string

    // basename of image used to run container
    Image       string

    // exposed, published ports
    Ports       []Port

    // XXX: configured image, run labels?
    Labels      map[string]string
}

func (self Container) String() string {
    return self.ID
}

type Port struct {
    Proto       string
    Port        uint16

    // exposed
    HostIP      string
    HostPort    uint16
}

type ContainerEvent struct {
    ID          string
    Status      string

    // Interpretation of running state after this event
    Running     bool

    // Current state of container; may be inconsistent or missing
    State       *Container
}

func (self ContainerEvent) String() string {
    return fmt.Sprintf("%s:%s", self.Status, self.ID)
}

func (self DockerConfig) Open() (*Docker, error) {
    d := &Docker{config: self}

    if err := d.open(); err != nil {
        return nil, err
    } else {
        return d, err
    }
}

func (self *Docker) open() error {
    var dockerClient *docker.Client
    var err error

    if self.config.Endpoint != "" {
        dockerClient, err = docker.NewClient(self.config.Endpoint)
    } else {
        dockerClient, err = docker.NewClientFromEnv()
    }

    if err != nil {
        return err
    } else {
        self.client = dockerClient
    }

    // Version
    if env, err := self.client.Version(); err != nil {
        return err
    } else {
        self.Version = env.Get("Version")
    }

    // Info
    if env, err := self.client.Info(); err != nil {
        return err
    } else {
        self.Name = env.Get("Name")
    }

    return nil
}

func (self *Docker) String() string {
    return fmt.Sprintf("Docker<%v>", self.config)
}

func parsePort(portString string) (uint16, error) {
    if portValue, err := strconv.Atoi(portString); err != nil {
        return 0, fmt.Errorf("port invalid: %v", portString)
    } else if portValue <= 0 || portValue >= (1 << 16) {
        return 0, fmt.Errorf("port out of range: %v", portString)
    } else {
        return uint16(portValue), nil
    }
}

/*
 * Return the state of the given container, based on the given event.
 *
 *  event       - /event status, or "" when listing
 */
func (self *Docker) inspectContainer(id string) (*Container, error) {
    dockerContainer, err := self.client.InspectContainer(id)
    if err != nil {
        log.Printf("%v.inspectContainer(%v): %v\n", self, id, err)
        return nil, err
    }

    state := Container{
        ID:         id,
        Name:       path.Base(dockerContainer.Name),
        Running:    dockerContainer.State.Running,
        IPv4:       net.ParseIP(dockerContainer.NetworkSettings.IPAddress),
        Hostname:   dockerContainer.Config.Hostname,
        Image:      path.Base(dockerContainer.Config.Image),
        Labels:     dockerContainer.Config.Labels,
    }

    for dockerPort, portBindings := range dockerContainer.NetworkSettings.Ports {
        port := Port{
            Proto:  dockerPort.Proto(),
        }

        if portValue, err := parsePort(dockerPort.Port()); err != nil {
            return nil, err
        } else {
            port.Port = portValue
        }

        for _, portBinding := range portBindings {
            // XXX: choose one
            port.HostIP = portBinding.HostIP

            if hostPort, err := parsePort(portBinding.HostPort); err != nil {
                return nil, err
            } else {
                port.HostPort = hostPort
            }
        }

        state.Ports = append(state.Ports, port)
    }

    return &state, nil
}

/*
 * Full list of (running) containers.
 *
 * TODO: somehow synchronize this with Subscribe() events to ensure consistency during listings?
 */
func (self *Docker) List() (out []*Container, err error) {
    containers, err := self.client.ListContainers(docker.ListContainersOptions{All: true})
    if err != nil {
        log.Printf("%v.ListContainers: %v\n", self, err)
        return nil, err
    }

    for _, listContainer := range containers {
        if containerState, err := self.inspectContainer(listContainer.ID); err != nil {
            break
        } else {
            out = append(out, containerState)
        }
    }

    return out, nil
}

// Handle a container event
func (self *Docker) containerEvent(dockerEvent *docker.APIEvents) (event ContainerEvent, err error) {
    event.ID = dockerEvent.ID
    event.Status = dockerEvent.Status

    if containerState, err := self.inspectContainer(dockerEvent.ID); err != nil {
        // skip lookup for cases where we don't have the container state anymore
        // this is normal for "destroy", but other events could also race
        event.State = nil

        // XXX: Running is indeterminite, but we can assume it is not?

    } else {
        event.Running = containerState.Running
        event.State = containerState
    }

    if dockerEvent.Status == "die" {
        // XXX: docker seems to be inconsistent about the inspected container State.Running=true/false immediately after a die?
        event.Running = false
    }

    return
}

/*
 * Subscribe to container events.
 */
func (self *Docker) Subscribe() (chan ContainerEvent, error) {
    listener := make(chan *docker.APIEvents)
    out := make(chan ContainerEvent)

    if err := self.client.AddEventListener(listener); err != nil {
        log.Printf("%v.Subscribe: AddEventListener\n", self, err)
        return nil, err
    }

    go func() {
        defer close(out)

        for dockerEvent := range listener {
            switch dockerEvent.Status {
            case "EOF":
                // XXX: how is this different to close()'ing the chan?
                log.Printf("%v.Subscribe: EOF\n", self)
                break

            // container events
            case "attach", "commit", "copy", "create", "destroy", "die", "exec_create", "exec_start", "export", "kill", "oom", "pause", "rename", "resize", "restart", "start", "stop", "top", "unpause":
                if containerEvent, err := self.containerEvent(dockerEvent); err != nil {
                    log.Printf("%v.Subscribe %v:%v: containerEvent: %v\n", self, dockerEvent.Status, dockerEvent.ID, err)

                } else {
                    // log.Printf("%v.Subscribe %v:%v: %#v\n", self, dockerEvent.Status, dockerEvent.ID, containerEvent)

                    out <- containerEvent
                }

            // image events
            case "delete", "import", "pull", "push", "tag", "untag":
                log.Printf("%v.Subscribe %v:%v: image event: ignore\n", self, dockerEvent.Status, dockerEvent.ID)

            default:
                log.Printf("%v.Subscribe %v:%v: unknown event: ignore\n", self, dockerEvent.Status, dockerEvent.ID)
            }
        }
    }()

    return out, nil
}
<|endoftext|>