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

package main

import (
	"bytes"
	"fmt"
	"net/http"
	"os"
	"sort"
	"strings"
	"time"
)

var (
	pendingLocal       bool // -l flag, use only local operations (no network)
	pendingCurrentOnly bool // -c flag, show only current branch
	pendingShort       bool // -s flag, short display
)

// A pendingBranch collects information about a single pending branch.
// We overlap the reading of this information for each branch.
type pendingBranch struct {
	*Branch            // standard Branch functionality
	current   bool     // is this the current branch?
	staged    []string // files in staging area, only if current==true
	unstaged  []string // files unstaged in local directory, only if current==true
	untracked []string // files untracked in local directory, only if current==true
}

// load populates b with information about the branch.
func (b *pendingBranch) load() {
	b.loadPending()
	if !b.current && b.commitsAhead == 0 {
		// Won't be displayed, don't bother looking any closer.
		return
	}
	b.OriginBranch() // cache result
	if b.current {
		b.staged, b.unstaged, b.untracked = LocalChanges()
	}
	for _, c := range b.Pending() {
		c.committed = nonBlankLines(cmdOutput("git", "diff", "--name-only", c.Parent, c.Hash, "--"))
		if !pendingLocal {
			c.g, c.gerr = b.GerritChange(c, "DETAILED_LABELS", "CURRENT_REVISION", "MESSAGES", "DETAILED_ACCOUNTS")
		}
		if c.g == nil {
			c.g = new(GerritChange) // easier for formatting code
		}
	}
}

func cmdPending(args []string) {
	flags.BoolVar(&pendingCurrentOnly, "c", false, "show only current branch")
	flags.BoolVar(&pendingLocal, "l", false, "use only local information - no network operations")
	flags.BoolVar(&pendingShort, "s", false, "show short listing")
	flags.Parse(args)
	if len(flags.Args()) > 0 {
		fmt.Fprintf(stderr(), "Usage: %s pending %s [-c] [-l] [-s]\n", os.Args[0], globalFlags)
		os.Exit(2)
	}

	// Fetch info about remote changes, so that we can say which branches need sync.
	if !pendingLocal {
		run("git", "fetch", "-q")
		http.DefaultClient.Timeout = 60 * time.Second
	}

	// Build list of pendingBranch structs to be filled in.
	// The current branch is always first.
	var branches []*pendingBranch
	branches = []*pendingBranch{{Branch: CurrentBranch(), current: true}}
	if !pendingCurrentOnly {
		current := CurrentBranch().Name
		for _, b := range LocalBranches() {
			if b.Name != current {
				branches = append(branches, &pendingBranch{Branch: b})
			}
		}
	}

	// The various data gathering is a little slow,
	// especially run in serial with a lot of branches.
	// Overlap inspection of multiple branches.
	// Each branch is only accessed by a single worker.

	// Build work queue.
	work := make(chan *pendingBranch, len(branches))
	done := make(chan bool, len(branches))
	for _, b := range branches {
		work <- b
	}
	close(work)

	// Kick off goroutines to do work.
	n := len(branches)
	if n > 10 {
		n = 10
	}
	for i := 0; i < n; i++ {
		go func() {
			for b := range work {
				b.load()
				done <- true
			}
		}()
	}

	// Wait for goroutines to finish.
	// Note: Counting work items, not goroutines (there may be fewer goroutines).
	for range branches {
		<-done
	}

	// Print output.
	// If there are multiple changes in the current branch, the output splits them out into separate sections,
	// in reverse commit order, to match git log output.
	//
	//	wbshadow 7a524a1..a496c1e (current branch, all mailed, 23 behind, tracking master)
	//	+ uncommitted changes
	//		Files unstaged:
	//			src/runtime/proc1.go
	//
	//	+ a496c1e https://go-review.googlesource.com/2064 (mailed)
	//		runtime: add missing write barriers in append's copy of slice data
	//
	//		Found with GODEBUG=wbshadow=1 mode.
	//		Eventually that will run automatically, but right now
	//		it still detects other missing write barriers.
	//
	//		Change-Id: Ic8624401d7c8225a935f719f96f2675c6f5c0d7c
	//
	//		Code-Review:
	//			+0 Austin Clements, Rick Hudson
	//		Files in this change:
	//			src/runtime/slice.go
	//
	//	+ 95390c7 https://go-review.googlesource.com/2061 (mailed)
	//		runtime: add GODEBUG wbshadow for finding missing write barriers
	//
	//		This is the detection code. It works well enough that I know of
	//		a handful of missing write barriers. However, those are subtle
	//		enough that I'll address them in separate followup CLs.
	//
	//		Change-Id: If863837308e7c50d96b5bdc7d65af4969bf53a6e
	//
	//		Code-Review:
	//			+0 Austin Clements, Rick Hudson
	//		Files in this change:
	//			src/runtime/extern.go
	//			src/runtime/malloc1.go
	//			src/runtime/malloc2.go
	//			src/runtime/mgc.go
	//			src/runtime/mgc0.go
	//			src/runtime/proc1.go
	//			src/runtime/runtime1.go
	//			src/runtime/runtime2.go
	//			src/runtime/stack1.go
	//
	// The first line only gives information that applies to the entire branch:
	// the name, the commit range, whether this is the current branch, whether
	// all the commits are mailed/submitted, how far behind, what remote branch
	// it is tracking.
	// The individual change sections have per-change information: the hash of that
	// commit, the URL on the Gerrit server, whether it is mailed/submitted, the list of
	// files in that commit. The uncommitted file modifications are shown as a separate
	// section, at the beginning, to fit better into the reverse commit order.
	//
	// The short view compresses the listing down to two lines per commit:
	//	wbshadow 7a524a1..a496c1e (current branch, all mailed, 23 behind, tracking master)
	//	+ uncommitted changes
	//		Files unstaged:
	//			src/runtime/proc1.go
	//	+ a496c1e runtime: add missing write barriers in append's copy of slice data (CL 2064, mailed)
	//	+ 95390c7 runtime: add GODEBUG wbshadow for finding missing write barriers (CL 2061, mailed)

	var buf bytes.Buffer
	printFileList := func(name string, list []string) {
		if len(list) == 0 {
			return
		}
		fmt.Fprintf(&buf, "\tFiles %s:\n", name)
		for _, file := range list {
			fmt.Fprintf(&buf, "\t\t%s\n", file)
		}
	}

	for _, b := range branches {
		if !b.current && b.commitsAhead == 0 {
			// Hide branches with no work on them.
			continue
		}

		fmt.Fprintf(&buf, "%s", b.Name)
		work := b.Pending()
		if len(work) > 0 {
			fmt.Fprintf(&buf, " %.7s..%s", b.branchpoint, work[0].ShortHash)
		}
		var tags []string
		if b.current {
			tags = append(tags, "current branch")
		}
		if allMailed(work) && len(work) > 0 {
			tags = append(tags, "all mailed")
		}
		if allSubmitted(work) && len(work) > 0 {
			tags = append(tags, "all submitted")
		}
		if b.commitsBehind > 0 {
			tags = append(tags, fmt.Sprintf("%d behind", b.commitsBehind))
		}
		if b.OriginBranch() != "origin/master" {
			tags = append(tags, "tracking "+strings.TrimPrefix(b.OriginBranch(), "origin/"))
		}
		if len(tags) > 0 {
			fmt.Fprintf(&buf, " (%s)", strings.Join(tags, ", "))
		}
		fmt.Fprintf(&buf, "\n")
		printed := false
		if text := b.errors(); text != "" {
			fmt.Fprintf(&buf, "\tERROR: %s\n", strings.Replace(strings.TrimSpace(text), "\n", "\n\t", -1))
			if !pendingShort {
				printed = true
				fmt.Fprintf(&buf, "\n")
			}
		}

		if b.current && len(b.staged)+len(b.unstaged)+len(b.untracked) > 0 {
			printed = true
			fmt.Fprintf(&buf, "+ uncommitted changes\n")
			printFileList("untracked", b.untracked)
			printFileList("unstaged", b.unstaged)
			printFileList("staged", b.staged)
			if !pendingShort {
				fmt.Fprintf(&buf, "\n")
			}
		}

		for _, c := range work {
			printed = true
			g := c.g
			msg := strings.TrimRight(c.Message, "\r\n")
			fmt.Fprintf(&buf, "+ %s", c.ShortHash)
			var tags []string
			if pendingShort {
				if i := strings.Index(msg, "\n"); i >= 0 {
					msg = msg[:i]
				}
				fmt.Fprintf(&buf, " %s", msg)
				if g.Number != 0 {
					tags = append(tags, fmt.Sprintf("CL %d%s", g.Number, codeReviewScores(g)))
				}
			} else {
				if g.Number != 0 {
					fmt.Fprintf(&buf, " %s/%d", auth.url, g.Number)
				}
			}
			if g.CurrentRevision == c.Hash {
				tags = append(tags, "mailed")
			}
			if g.Status == "MERGED" {
				tags = append(tags, "submitted")
			}
			if len(tags) > 0 {
				fmt.Fprintf(&buf, " (%s)", strings.Join(tags, ", "))
			}
			fmt.Fprintf(&buf, "\n")
			if pendingShort {
				continue
			}

			fmt.Fprintf(&buf, "\t%s\n", strings.Replace(msg, "\n", "\n\t", -1))
			fmt.Fprintf(&buf, "\n")

			for _, name := range g.LabelNames() {
				label := g.Labels[name]
				minValue := 10000
				maxValue := -10000
				byScore := map[int][]string{}
				for _, x := range label.All {
					// Hide CL owner unless owner score is nonzero.
					if g.Owner != nil && x.ID == g.Owner.ID && x.Value == 0 {
						continue
					}
					byScore[x.Value] = append(byScore[x.Value], x.Name)
					if minValue > x.Value {
						minValue = x.Value
					}
					if maxValue < x.Value {
						maxValue = x.Value
					}
				}
				// Unless there are scores to report, do not show labels other than Code-Review.
				// This hides Run-TryBot and TryBot-Result.
				if minValue >= 0 && maxValue <= 0 && name != "Code-Review" {
					continue
				}
				fmt.Fprintf(&buf, "\t%s:\n", name)
				for score := maxValue; score >= minValue; score-- {
					who := byScore[score]
					if len(who) == 0 || score == 0 && name != "Code-Review" {
						continue
					}
					sort.Strings(who)
					fmt.Fprintf(&buf, "\t\t%+d %s\n", score, strings.Join(who, ", "))
				}
			}

			printFileList("in this change", c.committed)
			fmt.Fprintf(&buf, "\n")
		}
		if pendingShort || !printed {
			fmt.Fprintf(&buf, "\n")
		}
	}

	stdout().Write(buf.Bytes())
}

// codeReviewScores reports the code review scores as tags for the short output.
func codeReviewScores(g *GerritChange) string {
	label := g.Labels["Code-Review"]
	if label == nil {
		return ""
	}
	minValue := 10000
	maxValue := -10000
	for _, x := range label.All {
		if minValue > x.Value {
			minValue = x.Value
		}
		if maxValue < x.Value {
			maxValue = x.Value
		}
	}
	var scores string
	if minValue < 0 {
		scores += fmt.Sprintf(" %d", minValue)
	}
	if maxValue > 0 {
		scores += fmt.Sprintf(" %+d", maxValue)
	}
	return scores
}

// allMailed reports whether all commits in work have been posted to Gerrit.
func allMailed(work []*Commit) bool {
	for _, c := range work {
		if c.Hash != c.g.CurrentRevision {
			return false
		}
	}
	return true
}

// allSubmitted reports whether all commits in work have been submitted to the origin branch.
func allSubmitted(work []*Commit) bool {
	for _, c := range work {
		if c.g.Status != "MERGED" {
			return false
		}
	}
	return true
}

// errors returns any errors that should be displayed
// about the state of the current branch, diagnosing common mistakes.
func (b *Branch) errors() string {
	b.loadPending()
	var buf bytes.Buffer
	if !b.IsLocalOnly() && b.commitsAhead > 0 {
		fmt.Fprintf(&buf, "Branch contains %d commit%s not on origin/%s.\n", b.commitsAhead, suffix(b.commitsAhead, "s"), b.Name)
		fmt.Fprintf(&buf, "\tDo not commit directly to %s branch.\n", b.Name)
	}
	return buf.String()
}

// suffix returns an empty string if n == 1, s otherwise.
func suffix(n int, s string) string {
	if n == 1 {
		return ""
	}
	return s
}
<commit_msg>git-codereview: factor pending commit formatting<commit_after>// Copyright 2014 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"sort"
	"strings"
	"time"
)

var (
	pendingLocal       bool // -l flag, use only local operations (no network)
	pendingCurrentOnly bool // -c flag, show only current branch
	pendingShort       bool // -s flag, short display
)

// A pendingBranch collects information about a single pending branch.
// We overlap the reading of this information for each branch.
type pendingBranch struct {
	*Branch            // standard Branch functionality
	current   bool     // is this the current branch?
	staged    []string // files in staging area, only if current==true
	unstaged  []string // files unstaged in local directory, only if current==true
	untracked []string // files untracked in local directory, only if current==true
}

// load populates b with information about the branch.
func (b *pendingBranch) load() {
	b.loadPending()
	if !b.current && b.commitsAhead == 0 {
		// Won't be displayed, don't bother looking any closer.
		return
	}
	b.OriginBranch() // cache result
	if b.current {
		b.staged, b.unstaged, b.untracked = LocalChanges()
	}
	for _, c := range b.Pending() {
		c.committed = nonBlankLines(cmdOutput("git", "diff", "--name-only", c.Parent, c.Hash, "--"))
		if !pendingLocal {
			c.g, c.gerr = b.GerritChange(c, "DETAILED_LABELS", "CURRENT_REVISION", "MESSAGES", "DETAILED_ACCOUNTS")
		}
		if c.g == nil {
			c.g = new(GerritChange) // easier for formatting code
		}
	}
}

func cmdPending(args []string) {
	flags.BoolVar(&pendingCurrentOnly, "c", false, "show only current branch")
	flags.BoolVar(&pendingLocal, "l", false, "use only local information - no network operations")
	flags.BoolVar(&pendingShort, "s", false, "show short listing")
	flags.Parse(args)
	if len(flags.Args()) > 0 {
		fmt.Fprintf(stderr(), "Usage: %s pending %s [-c] [-l] [-s]\n", os.Args[0], globalFlags)
		os.Exit(2)
	}

	// Fetch info about remote changes, so that we can say which branches need sync.
	if !pendingLocal {
		run("git", "fetch", "-q")
		http.DefaultClient.Timeout = 60 * time.Second
	}

	// Build list of pendingBranch structs to be filled in.
	// The current branch is always first.
	var branches []*pendingBranch
	branches = []*pendingBranch{{Branch: CurrentBranch(), current: true}}
	if !pendingCurrentOnly {
		current := CurrentBranch().Name
		for _, b := range LocalBranches() {
			if b.Name != current {
				branches = append(branches, &pendingBranch{Branch: b})
			}
		}
	}

	// The various data gathering is a little slow,
	// especially run in serial with a lot of branches.
	// Overlap inspection of multiple branches.
	// Each branch is only accessed by a single worker.

	// Build work queue.
	work := make(chan *pendingBranch, len(branches))
	done := make(chan bool, len(branches))
	for _, b := range branches {
		work <- b
	}
	close(work)

	// Kick off goroutines to do work.
	n := len(branches)
	if n > 10 {
		n = 10
	}
	for i := 0; i < n; i++ {
		go func() {
			for b := range work {
				b.load()
				done <- true
			}
		}()
	}

	// Wait for goroutines to finish.
	// Note: Counting work items, not goroutines (there may be fewer goroutines).
	for range branches {
		<-done
	}

	// Print output.
	// If there are multiple changes in the current branch, the output splits them out into separate sections,
	// in reverse commit order, to match git log output.
	//
	//	wbshadow 7a524a1..a496c1e (current branch, all mailed, 23 behind, tracking master)
	//	+ uncommitted changes
	//		Files unstaged:
	//			src/runtime/proc1.go
	//
	//	+ a496c1e https://go-review.googlesource.com/2064 (mailed)
	//		runtime: add missing write barriers in append's copy of slice data
	//
	//		Found with GODEBUG=wbshadow=1 mode.
	//		Eventually that will run automatically, but right now
	//		it still detects other missing write barriers.
	//
	//		Change-Id: Ic8624401d7c8225a935f719f96f2675c6f5c0d7c
	//
	//		Code-Review:
	//			+0 Austin Clements, Rick Hudson
	//		Files in this change:
	//			src/runtime/slice.go
	//
	//	+ 95390c7 https://go-review.googlesource.com/2061 (mailed)
	//		runtime: add GODEBUG wbshadow for finding missing write barriers
	//
	//		This is the detection code. It works well enough that I know of
	//		a handful of missing write barriers. However, those are subtle
	//		enough that I'll address them in separate followup CLs.
	//
	//		Change-Id: If863837308e7c50d96b5bdc7d65af4969bf53a6e
	//
	//		Code-Review:
	//			+0 Austin Clements, Rick Hudson
	//		Files in this change:
	//			src/runtime/extern.go
	//			src/runtime/malloc1.go
	//			src/runtime/malloc2.go
	//			src/runtime/mgc.go
	//			src/runtime/mgc0.go
	//			src/runtime/proc1.go
	//			src/runtime/runtime1.go
	//			src/runtime/runtime2.go
	//			src/runtime/stack1.go
	//
	// The first line only gives information that applies to the entire branch:
	// the name, the commit range, whether this is the current branch, whether
	// all the commits are mailed/submitted, how far behind, what remote branch
	// it is tracking.
	// The individual change sections have per-change information: the hash of that
	// commit, the URL on the Gerrit server, whether it is mailed/submitted, the list of
	// files in that commit. The uncommitted file modifications are shown as a separate
	// section, at the beginning, to fit better into the reverse commit order.
	//
	// The short view compresses the listing down to two lines per commit:
	//	wbshadow 7a524a1..a496c1e (current branch, all mailed, 23 behind, tracking master)
	//	+ uncommitted changes
	//		Files unstaged:
	//			src/runtime/proc1.go
	//	+ a496c1e runtime: add missing write barriers in append's copy of slice data (CL 2064, mailed)
	//	+ 95390c7 runtime: add GODEBUG wbshadow for finding missing write barriers (CL 2061, mailed)

	var buf bytes.Buffer
	printFileList := func(name string, list []string) {
		if len(list) == 0 {
			return
		}
		fmt.Fprintf(&buf, "\tFiles %s:\n", name)
		for _, file := range list {
			fmt.Fprintf(&buf, "\t\t%s\n", file)
		}
	}

	for _, b := range branches {
		if !b.current && b.commitsAhead == 0 {
			// Hide branches with no work on them.
			continue
		}

		fmt.Fprintf(&buf, "%s", b.Name)
		work := b.Pending()
		if len(work) > 0 {
			fmt.Fprintf(&buf, " %.7s..%s", b.branchpoint, work[0].ShortHash)
		}
		var tags []string
		if b.current {
			tags = append(tags, "current branch")
		}
		if allMailed(work) && len(work) > 0 {
			tags = append(tags, "all mailed")
		}
		if allSubmitted(work) && len(work) > 0 {
			tags = append(tags, "all submitted")
		}
		if b.commitsBehind > 0 {
			tags = append(tags, fmt.Sprintf("%d behind", b.commitsBehind))
		}
		if b.OriginBranch() != "origin/master" {
			tags = append(tags, "tracking "+strings.TrimPrefix(b.OriginBranch(), "origin/"))
		}
		if len(tags) > 0 {
			fmt.Fprintf(&buf, " (%s)", strings.Join(tags, ", "))
		}
		fmt.Fprintf(&buf, "\n")
		printed := false
		if text := b.errors(); text != "" {
			fmt.Fprintf(&buf, "\tERROR: %s\n", strings.Replace(strings.TrimSpace(text), "\n", "\n\t", -1))
			if !pendingShort {
				printed = true
				fmt.Fprintf(&buf, "\n")
			}
		}

		if b.current && len(b.staged)+len(b.unstaged)+len(b.untracked) > 0 {
			printed = true
			fmt.Fprintf(&buf, "+ uncommitted changes\n")
			printFileList("untracked", b.untracked)
			printFileList("unstaged", b.unstaged)
			printFileList("staged", b.staged)
			if !pendingShort {
				fmt.Fprintf(&buf, "\n")
			}
		}

		for _, c := range work {
			printed = true
			fmt.Fprintf(&buf, "+ ")
			formatCommit(&buf, c, pendingShort)
			if !pendingShort {
				printFileList("in this change", c.committed)
				fmt.Fprintf(&buf, "\n")
			}
		}
		if pendingShort || !printed {
			fmt.Fprintf(&buf, "\n")
		}
	}

	stdout().Write(buf.Bytes())
}

// formatCommit writes detailed information about c to w. c.g must
// have the "CURRENT_REVISION" (or "ALL_REVISIONS") and
// "DETAILED_LABELS" options set.
//
// If short is true, this writes a single line overview.
//
// If short is false, this writes detailed information about the
// commit and its Gerrit state.
func formatCommit(w io.Writer, c *Commit, short bool) {
	g := c.g
	msg := strings.TrimRight(c.Message, "\r\n")
	fmt.Fprintf(w, "%s", c.ShortHash)
	var tags []string
	if short {
		if i := strings.Index(msg, "\n"); i >= 0 {
			msg = msg[:i]
		}
		fmt.Fprintf(w, " %s", msg)
		if g.Number != 0 {
			tags = append(tags, fmt.Sprintf("CL %d%s", g.Number, codeReviewScores(g)))
		}
	} else {
		if g.Number != 0 {
			fmt.Fprintf(w, " %s/%d", auth.url, g.Number)
		}
	}
	if g.CurrentRevision == c.Hash {
		tags = append(tags, "mailed")
	}
	if g.Status == "MERGED" {
		tags = append(tags, "submitted")
	}
	if len(tags) > 0 {
		fmt.Fprintf(w, " (%s)", strings.Join(tags, ", "))
	}
	fmt.Fprintf(w, "\n")
	if short {
		return
	}

	fmt.Fprintf(w, "\t%s\n", strings.Replace(msg, "\n", "\n\t", -1))
	fmt.Fprintf(w, "\n")

	for _, name := range g.LabelNames() {
		label := g.Labels[name]
		minValue := 10000
		maxValue := -10000
		byScore := map[int][]string{}
		for _, x := range label.All {
			// Hide CL owner unless owner score is nonzero.
			if g.Owner != nil && x.ID == g.Owner.ID && x.Value == 0 {
				continue
			}
			byScore[x.Value] = append(byScore[x.Value], x.Name)
			if minValue > x.Value {
				minValue = x.Value
			}
			if maxValue < x.Value {
				maxValue = x.Value
			}
		}
		// Unless there are scores to report, do not show labels other than Code-Review.
		// This hides Run-TryBot and TryBot-Result.
		if minValue >= 0 && maxValue <= 0 && name != "Code-Review" {
			continue
		}
		fmt.Fprintf(w, "\t%s:\n", name)
		for score := maxValue; score >= minValue; score-- {
			who := byScore[score]
			if len(who) == 0 || score == 0 && name != "Code-Review" {
				continue
			}
			sort.Strings(who)
			fmt.Fprintf(w, "\t\t%+d %s\n", score, strings.Join(who, ", "))
		}
	}
}

// codeReviewScores reports the code review scores as tags for the short output.
//
// g must have the "DETAILED_LABELS" option set.
func codeReviewScores(g *GerritChange) string {
	label := g.Labels["Code-Review"]
	if label == nil {
		return ""
	}
	minValue := 10000
	maxValue := -10000
	for _, x := range label.All {
		if minValue > x.Value {
			minValue = x.Value
		}
		if maxValue < x.Value {
			maxValue = x.Value
		}
	}
	var scores string
	if minValue < 0 {
		scores += fmt.Sprintf(" %d", minValue)
	}
	if maxValue > 0 {
		scores += fmt.Sprintf(" %+d", maxValue)
	}
	return scores
}

// allMailed reports whether all commits in work have been posted to Gerrit.
func allMailed(work []*Commit) bool {
	for _, c := range work {
		if c.Hash != c.g.CurrentRevision {
			return false
		}
	}
	return true
}

// allSubmitted reports whether all commits in work have been submitted to the origin branch.
func allSubmitted(work []*Commit) bool {
	for _, c := range work {
		if c.g.Status != "MERGED" {
			return false
		}
	}
	return true
}

// errors returns any errors that should be displayed
// about the state of the current branch, diagnosing common mistakes.
func (b *Branch) errors() string {
	b.loadPending()
	var buf bytes.Buffer
	if !b.IsLocalOnly() && b.commitsAhead > 0 {
		fmt.Fprintf(&buf, "Branch contains %d commit%s not on origin/%s.\n", b.commitsAhead, suffix(b.commitsAhead, "s"), b.Name)
		fmt.Fprintf(&buf, "\tDo not commit directly to %s branch.\n", b.Name)
	}
	return buf.String()
}

// suffix returns an empty string if n == 1, s otherwise.
func suffix(n int, s string) string {
	if n == 1 {
		return ""
	}
	return s
}
<|endoftext|>


<commit_before>package main

import "github.com/google/go-github/github"

type reposCreate struct {
	Name   string `cli:"arg required"`
	Public bool   `cli:"opt --public"`
	Orga   string `cli:"opt --orga"`
	Teams  []int  `cli:"opt --teams"`
}

func (r *reposCreate) Run() error {
	cl, err := newClient()
	if err != nil {
		return err
	}
	repo := &github.Repository{Name: s2p(r.Name), Private: b2p(!r.Public)}

	if _, _, err := cl.Repositories.Create(r.Orga, repo); err != nil {
		return err
	}
	for _, t := range r.Teams {
		_, err = cl.Organizations.AddTeamRepo(t, r.Orga, r.Name, &github.OrganizationAddTeamRepoOptions{Permission: "pull"})
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>gh repos/create: grant push access for team members (use --read-only to pull only)<commit_after>package main

import "github.com/google/go-github/github"

type reposCreate struct {
	Name     string `cli:"arg required"`
	Public   bool   `cli:"opt --public"`
	Orga     string `cli:"opt --orga"`
	Teams    []int  `cli:"opt --teams"`
	ReadOnly bool   `cli:"opt --read-only desc='Add teams with read only'"`
}

func (r *reposCreate) Run() error {
	cl, err := newClient()
	if err != nil {
		return err
	}
	repo := &github.Repository{Name: s2p(r.Name), Private: b2p(!r.Public)}

	if _, _, err := cl.Repositories.Create(r.Orga, repo); err != nil {
		return err
	}
	permission := "push"
	if r.ReadOnly {
		permission = "pull"
	}
	for _, t := range r.Teams {
		_, err = cl.Organizations.AddTeamRepo(t, r.Orga, r.Name, &github.OrganizationAddTeamRepoOptions{Permission: permission})
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


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

package mixnet

import (
	"crypto/rand"
	"encoding/binary"
	"errors"
	"io"

	"golang.org/x/crypto/nacl/box"

	"github.com/jlmucb/cloudproxy/go/tao"
)

// A circuit carries cells
type Circuit struct {
	conn  *Conn
	id    uint64
	cells chan []byte
	errs  chan error
	next  *Circuit
	prev  *Circuit

	peerKey    *[32]byte
	publicKey  *[32]byte
	privateKey *[32]byte
	sharedKey  *[32]byte
}

// SET THE CORRECT KEYS
func NewCircuit(conn *Conn, id uint64, peerKey, publicKey, privateKey *[32]byte) *Circuit {
	if peerKey != nil && privateKey != nil {
		var sharedKey [32]byte
		box.Precompute(&sharedKey, peerKey, privateKey)
		return &Circuit{
			conn:  conn,
			id:    id,
			cells: make(chan []byte, 2),
			errs:  make(chan error, 2),

			peerKey:    peerKey,
			publicKey:  publicKey,
			privateKey: privateKey,
			sharedKey:  &sharedKey,
		}
	} else {
		return &Circuit{
			conn:  conn,
			id:    id,
			cells: make(chan []byte, 2),
			errs:  make(chan error, 2),
		}
	}
}

func (c *Circuit) Encrypt(msg []byte) []byte {
	var nonce [24]byte
	rand.Read(nonce[:])
	boxed := box.SealAfterPrecomputation(nil, msg, &nonce, c.sharedKey)
	boxed = append(nonce[:], boxed...)
	return boxed
}

func (c *Circuit) Decrypt(boxed []byte) ([]byte, bool) {
	var nonce [24]byte
	copy(nonce[:], boxed[:24])
	return box.OpenAfterPrecomputation(nil, boxed[24:], &nonce, c.sharedKey)
}

func (c *Circuit) Write(msg []byte) (int, error) {
	// No need to multiplex writes
	return c.conn.Write(msg)
}

func (c *Circuit) BufferCell(cell []byte, err error) {
	c.cells <- cell
	c.errs <- err
}

func (c *Circuit) Read(msg []byte) (int, error) {
	cell, ok1 := <-c.cells
	err, ok2 := <-c.errs
	if !ok1 || !ok2 {
		return 0, io.EOF
	} else if err != nil {
		return 0, err
	}
	n := copy(msg, cell)
	return n, nil
}

func (c *Circuit) Close() error {
	close(c.cells)
	return nil
}

func breakMessages(msg []byte, res chan []byte) {
	body := make([]byte, BODY_SIZE)
	binary.LittleEndian.PutUint64(body, uint64(len(msg)))
	bytes := copy(body[LEN_SIZE:], msg)
	res <- body
	for bytes < len(msg) {
		body := make([]byte, BODY_SIZE)
		bytes += copy(body, msg[bytes:])
		res <- body
	}
	close(res)
}

// SendMessage divides a message into cells and sends each cell over the network
// connection. A message is signaled to the receiver by the first byte of the
// first cell. The next few bytes encode the total number of bytes in the
// message.
func (c *Circuit) SendMessage(msg []byte) error {
	cell := make([]byte, CellBytes)
	binary.LittleEndian.PutUint64(cell[ID:], c.id)
	cell[TYPE] = msgCell

	bodies := make(chan []byte)

	go breakMessages(msg, bodies)
	for {
		body, ok := <-bodies
		if !ok {
			break
		}
		boxed := c.Encrypt(body)
		copy(cell[BODY:], boxed)
		if _, err := c.Write(cell); err != nil {
			return err
		}
	}
	return nil
}

// SendDirective serializes and pads a directive to the length of a cell and
// sends it to the peer. A directive is signaled to the receiver by the first
// byte of the cell. The next few bytes encode the length of of the serialized
// protocol buffer. If the buffer doesn't fit in a cell, then throw an error.
func (c *Circuit) SendDirective(d *Directive) (int, error) {
	cell, err := marshalDirective(c.id, d)
	if err != nil {
		return 0, err
	}
	return c.Write(cell)
}

// ReceiveMessage reads message cells from the router and assembles them into
// a messsage.
func (c *Circuit) ReceiveMessage() ([]byte, error) {
	// Receive cells from router.
	cell := make([]byte, CellBytes)
	n, err := c.Read(cell)
	if err != nil {
		return nil, err
	}

	if cell[TYPE] == dirCell {
		var d Directive
		if err = unmarshalDirective(cell, &d); err != nil {
			return nil, err
		}
		if *d.Type == DirectiveType_ERROR {
			return nil, errors.New("router error: " + (*d.Error))
		}
		return nil, errCellType
	} else if cell[TYPE] != msgCell {
		return nil, errCellType
	}

	boxed := cell[BODY:n]
	if len(boxed) > 0 {
		body, ok := c.Decrypt(boxed)
		if !ok {
			return nil, errors.New("Misauthenticated ciphertext")
		}

		msgBytes := binary.LittleEndian.Uint64(body)
		if msgBytes > MaxMsgBytes {
			return nil, errMsgLength
		}

		msg := make([]byte, msgBytes)
		bytes := copy(msg, body[LEN_SIZE:])

		for uint64(bytes) < msgBytes {
			tao.ZeroBytes(cell)
			n, err = c.Read(cell)
			if err != nil {
				return nil, err
			} else if cell[TYPE] != msgCell {
				return nil, errCellType
			}
			boxed := cell[BODY:n]
			body, _ := c.Decrypt(boxed)
			bytes += copy(msg[bytes:], body)
		}
		return msg, nil
	} else {
		return nil, nil
	}
}

// ReceiveDirective awaits a reply from the peer and returns the directive
// received, e.g. in response to RouterContext.HandleProxy(). If the directive
// type is ERROR, return an error.
func (c *Circuit) ReceiveDirective(d *Directive) error {
	cell := make([]byte, CellBytes)
	_, err := c.Read(cell)
	if err != nil {
		return err
	}

	err = unmarshalDirective(cell, d)
	if err != nil {
		return err
	}

	if *d.Type == DirectiveType_ERROR {
		return errors.New("router error: " + (*d.Error))
	}
	return nil
}
<commit_msg>minior error check..<commit_after>// Copyright (c) 2016, Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mixnet

import (
	"crypto/rand"
	"encoding/binary"
	"errors"
	"io"

	"golang.org/x/crypto/nacl/box"

	"github.com/jlmucb/cloudproxy/go/tao"
)

// A circuit carries cells
type Circuit struct {
	conn  *Conn
	id    uint64
	cells chan []byte
	errs  chan error
	next  *Circuit
	prev  *Circuit

	peerKey    *[32]byte
	publicKey  *[32]byte
	privateKey *[32]byte
	sharedKey  *[32]byte
}

// SET THE CORRECT KEYS
func NewCircuit(conn *Conn, id uint64, peerKey, publicKey, privateKey *[32]byte) *Circuit {
	if peerKey != nil && privateKey != nil {
		var sharedKey [32]byte
		box.Precompute(&sharedKey, peerKey, privateKey)
		return &Circuit{
			conn:  conn,
			id:    id,
			cells: make(chan []byte, 2),
			errs:  make(chan error, 2),

			peerKey:    peerKey,
			publicKey:  publicKey,
			privateKey: privateKey,
			sharedKey:  &sharedKey,
		}
	} else {
		return &Circuit{
			conn:  conn,
			id:    id,
			cells: make(chan []byte, 2),
			errs:  make(chan error, 2),
		}
	}
}

func (c *Circuit) Encrypt(msg []byte) []byte {
	var nonce [24]byte
	rand.Read(nonce[:])
	boxed := box.SealAfterPrecomputation(nil, msg, &nonce, c.sharedKey)
	boxed = append(nonce[:], boxed...)
	return boxed
}

func (c *Circuit) Decrypt(boxed []byte) ([]byte, bool) {
	var nonce [24]byte
	copy(nonce[:], boxed[:24])
	return box.OpenAfterPrecomputation(nil, boxed[24:], &nonce, c.sharedKey)
}

func (c *Circuit) Write(msg []byte) (int, error) {
	// No need to multiplex writes
	return c.conn.Write(msg)
}

func (c *Circuit) BufferCell(cell []byte, err error) {
	c.cells <- cell
	c.errs <- err
}

func (c *Circuit) Read(msg []byte) (int, error) {
	cell, ok1 := <-c.cells
	err, ok2 := <-c.errs
	if !ok1 || !ok2 {
		return 0, io.EOF
	} else if err != nil {
		return 0, err
	}
	n := copy(msg, cell)
	return n, nil
}

func (c *Circuit) Close() error {
	close(c.cells)
	return nil
}

func breakMessages(msg []byte, res chan []byte) {
	body := make([]byte, BODY_SIZE)
	binary.LittleEndian.PutUint64(body, uint64(len(msg)))
	bytes := copy(body[LEN_SIZE:], msg)
	res <- body
	for bytes < len(msg) {
		body := make([]byte, BODY_SIZE)
		bytes += copy(body, msg[bytes:])
		res <- body
	}
	close(res)
}

// SendMessage divides a message into cells and sends each cell over the network
// connection. A message is signaled to the receiver by the first byte of the
// first cell. The next few bytes encode the total number of bytes in the
// message.
func (c *Circuit) SendMessage(msg []byte) error {
	cell := make([]byte, CellBytes)
	binary.LittleEndian.PutUint64(cell[ID:], c.id)
	cell[TYPE] = msgCell

	bodies := make(chan []byte)

	go breakMessages(msg, bodies)
	for {
		body, ok := <-bodies
		if !ok {
			break
		}
		boxed := c.Encrypt(body)
		copy(cell[BODY:], boxed)
		if _, err := c.Write(cell); err != nil {
			return err
		}
	}
	return nil
}

// SendDirective serializes and pads a directive to the length of a cell and
// sends it to the peer. A directive is signaled to the receiver by the first
// byte of the cell. The next few bytes encode the length of of the serialized
// protocol buffer. If the buffer doesn't fit in a cell, then throw an error.
func (c *Circuit) SendDirective(d *Directive) (int, error) {
	cell, err := marshalDirective(c.id, d)
	if err != nil {
		return 0, err
	}
	return c.Write(cell)
}

// ReceiveMessage reads message cells from the router and assembles them into
// a messsage.
func (c *Circuit) ReceiveMessage() ([]byte, error) {
	// Receive cells from router.
	cell := make([]byte, CellBytes)
	n, err := c.Read(cell)
	if err != nil {
		return nil, err
	}

	if cell[TYPE] == dirCell {
		var d Directive
		if err = unmarshalDirective(cell, &d); err != nil {
			return nil, err
		}
		if *d.Type == DirectiveType_ERROR {
			return nil, errors.New("router error: " + (*d.Error))
		}
		return nil, errCellType
	} else if cell[TYPE] != msgCell {
		return nil, errCellType
	}

	boxed := cell[BODY:n]
	if len(boxed) > 0 {
		body, ok := c.Decrypt(boxed)
		if !ok {
			return nil, errors.New("Misauthenticated ciphertext")
		}

		msgBytes := binary.LittleEndian.Uint64(body)
		if msgBytes > MaxMsgBytes {
			return nil, errMsgLength
		}

		msg := make([]byte, msgBytes)
		bytes := copy(msg, body[LEN_SIZE:])

		for uint64(bytes) < msgBytes {
			tao.ZeroBytes(cell)
			n, err = c.Read(cell)
			if err != nil {
				return nil, err
			} else if cell[TYPE] != msgCell {
				return nil, errCellType
			}
			boxed := cell[BODY:n]
			body, ok := c.Decrypt(boxed)
			if !ok {
				return nil, errors.New("Misauthenticated ciphertext")
			}
			bytes += copy(msg[bytes:], body)
		}
		return msg, nil
	} else {
		return nil, nil
	}
}

// ReceiveDirective awaits a reply from the peer and returns the directive
// received, e.g. in response to RouterContext.HandleProxy(). If the directive
// type is ERROR, return an error.
func (c *Circuit) ReceiveDirective(d *Directive) error {
	cell := make([]byte, CellBytes)
	_, err := c.Read(cell)
	if err != nil {
		return err
	}

	err = unmarshalDirective(cell, d)
	if err != nil {
		return err
	}

	if *d.Type == DirectiveType_ERROR {
		return errors.New("router error: " + (*d.Error))
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

type CmdPGPDrop struct {
	libkb.Contextified
	id keybase1.KID
}

func (c *CmdPGPDrop) ParseArgv(ctx *cli.Context) (err error) {
	if len(ctx.Args()) != 1 {
		return fmt.Errorf("drop takes exactly one key")
	}
	c.id, err = keybase1.KIDFromStringChecked(ctx.Args()[0])
	if err != nil {
		return fmt.Errorf("bad key: %v", err)
	}
	return nil
}

func (c *CmdPGPDrop) Run() (err error) {
	cli, err := GetRevokeClient(c.G())
	if err != nil {
		return err
	}

	protocols := []rpc.Protocol{
		NewSecretUIProtocol(c.G()),
	}
	if err = RegisterProtocolsWithContext(protocols, c.G()); err != nil {
		return err
	}

	return cli.RevokeKey(context.TODO(), keybase1.RevokeKeyArg{
		KeyID: c.id,
	})
}

func NewCmdPGPDrop(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "drop",
		ArgumentHelp: "<key-id>",
		Usage:        "Drop Keybase's use of a PGP key",
		Flags:        []cli.Flag{},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdPGPDrop{}, "drop", c)
		},
		Description: `"keybase pgp drop" signs a statement saying the given PGP
   key should no longer be associated with this account. It will **not** sign a PGP-style
   revocation cert for this key; you'll have to do that on your own.`,
	}
}

func (c *CmdPGPDrop) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:     true,
		GpgKeyring: true,
		KbKeyring:  true,
		API:        true,
	}
}
<commit_msg>Fix #9076 (#9077)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

type CmdPGPDrop struct {
	libkb.Contextified
	id keybase1.KID
}

func (c *CmdPGPDrop) ParseArgv(ctx *cli.Context) (err error) {
	if len(ctx.Args()) != 1 {
		return fmt.Errorf("drop takes exactly one key")
	}
	c.id, err = keybase1.KIDFromStringChecked(ctx.Args()[0])
	if err != nil {
		return fmt.Errorf("bad key: %v", err)
	}
	return nil
}

func (c *CmdPGPDrop) Run() (err error) {
	cli, err := GetRevokeClient(c.G())
	if err != nil {
		return err
	}

	protocols := []rpc.Protocol{
		NewSecretUIProtocol(c.G()),
	}
	if err = RegisterProtocolsWithContext(protocols, c.G()); err != nil {
		return err
	}

	return cli.RevokeKey(context.TODO(), keybase1.RevokeKeyArg{
		KeyID: c.id,
	})
}

func NewCmdPGPDrop(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "drop",
		ArgumentHelp: "<key-id>",
		Usage:        "Drop Keybase's use of a PGP key",
		Flags:        []cli.Flag{},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdPGPDrop{Contextified: libkb.NewContextified(g)}, "drop", c)
		},
		Description: `"keybase pgp drop" signs a statement saying the given PGP
   key should no longer be associated with this account. It will **not** sign a PGP-style
   revocation cert for this key; you'll have to do that on your own.`,
	}
}

func (c *CmdPGPDrop) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:     true,
		GpgKeyring: true,
		KbKeyring:  true,
		API:        true,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"

	"github.com/youtube/vitess/go/vt/health"
	"github.com/youtube/vitess/go/vt/servenv"
	_ "github.com/youtube/vitess/go/vt/status"
	"github.com/youtube/vitess/go/vt/tabletmanager"
	"github.com/youtube/vitess/go/vt/tabletserver"
)

var (
	// tabletTemplate contains the style sheet and the tablet itself.
	tabletTemplate = `

Alias: {{github_com_youtube_vitess_vtctld_tablet .Alias.String}}

Keyspace: {{github_com_youtube_vitess_vtctld_keyspace .Keyspace}} Shard: {{github_com_youtube_vitess_vtctld_shard .Keyspace .Shard}}

Serving graph: {{github_com_youtube_vitess_vtctld_srv_keyspace .Alias.Cell .Keyspace}} {{github_com_youtube_vitess_vtctld_srv_shard .Alias.Cell .Keyspace .Shard}} {{github_com_youtube_vitess_vtctld_srv_type .Alias.Cell .Keyspace .Shard .Type}}

Replication graph: {{github_com_youtube_vitess_vtctld_replication .Alias.Cell .Keyspace .Shard}}

State: {{.State}}

`

	// healthTemplate is just about the tablet health
	healthTemplate = `

Current status: {{.Current}}

Polling health information from {{github_com_youtube_vitess_health_html_name}}.

History

    Time
    State

  {{range .Records}}

    {{.Time.Format "Jan 2, 2006 at 15:04:05 (MST)"}}

    {{ if eq "unhealthy" .Class}}
      unhealthy: {{.Error}}
    {{else if eq "unhappy" .Class}}
      unhappy (reasons: {{range $key, $value := .Result}}{{$key}}: {{$value}} {{end}})
    {{else}}
      healthy
    {{end}}

  {{end}}

healthy

serving traffic.

unhappy

will serve traffic only if there are no fully healthy tablets.

unhealthy

will not serve traffic.

`

	// binlogTemplate is about the binlog players
	binlogTemplate = `
{{if .Controllers}}
Binlog player state: {{.State}}

    Index
    SourceShard
    StopAtGroupId
    LastGroupId
    Counts
    Rates

  {{range .Controllers}}

      {{.Index}}
      {{.SourceShard.AsHTML}}
      {{if .StopAtGroupId}}{{.StopAtGroupId}}{{end}}
      {{.LastGroupId}}
      {{range $key, $value := .Counts}}{{$key}}: {{$value}}
{{end}}
      {{range $key, $values := .Rates}}{{$key}}: {{range $values}}{{.}} {{end}}
{{end}}

  {{end}}

{{else}}
No binlog player is running.
{{end}}
`
)

type healthStatus struct {
	Records []interface{}
}

func (hs *healthStatus) Current() string {
	if len(hs.Records) > 0 {
		return hs.Records[0].(*tabletmanager.HealthRecord).Class()
	}
	return "unknown"
}

func healthHTMLName() template.HTML {
	return health.HTMLName()
}

func init() {
	servenv.OnRun(func() {
		servenv.AddStatusPart("Tablet", tabletTemplate, func() interface{} {
			return agent.Tablet()
		})
		if agent.IsRunningHealthCheck() {
			servenv.AddStatusFuncs(template.FuncMap{
				"github_com_youtube_vitess_health_html_name": healthHTMLName,
			})
			servenv.AddStatusPart("Health", healthTemplate, func() interface{} {
				return &healthStatus{Records: agent.History.Records()}
			})
		}
		tabletserver.AddStatusPart()
		servenv.AddStatusPart("Binlog Player", binlogTemplate, func() interface{} {
			return agent.BinlogPlayerMap.Status()
		})
	})
}
<commit_msg>Adding links to tablet info.<commit_after>package main

import (
	"html/template"

	"github.com/youtube/vitess/go/vt/health"
	"github.com/youtube/vitess/go/vt/servenv"
	_ "github.com/youtube/vitess/go/vt/status"
	"github.com/youtube/vitess/go/vt/tabletmanager"
	"github.com/youtube/vitess/go/vt/tabletserver"
)

var (
	// tabletTemplate contains the style sheet and the tablet itself.
	tabletTemplate = `

      Alias: {{github_com_youtube_vitess_vtctld_tablet .Alias.String}}

      Keyspace: {{github_com_youtube_vitess_vtctld_keyspace .Keyspace}} Shard: {{github_com_youtube_vitess_vtctld_shard .Keyspace .Shard}}

      Serving graph: {{github_com_youtube_vitess_vtctld_srv_keyspace .Alias.Cell .Keyspace}} {{github_com_youtube_vitess_vtctld_srv_shard .Alias.Cell .Keyspace .Shard}} {{github_com_youtube_vitess_vtctld_srv_type .Alias.Cell .Keyspace .Shard .Type}}

      Replication graph: {{github_com_youtube_vitess_vtctld_replication .Alias.Cell .Keyspace .Shard}}

      State: {{.State}}

      Schema

      Schema Query Plans

      Schema Query Stats

      Schema Table Stats

      Query Stats

      Consolidations

      Current Query Log

      Current Transaction Log

      Health Check

      Memcache

`

	// healthTemplate is just about the tablet health
	healthTemplate = `

Current status: {{.Current}}

Polling health information from {{github_com_youtube_vitess_health_html_name}}.

History

    Time
    State

  {{range .Records}}

    {{.Time.Format "Jan 2, 2006 at 15:04:05 (MST)"}}

    {{ if eq "unhealthy" .Class}}
      unhealthy: {{.Error}}
    {{else if eq "unhappy" .Class}}
      unhappy (reasons: {{range $key, $value := .Result}}{{$key}}: {{$value}} {{end}})
    {{else}}
      healthy
    {{end}}

  {{end}}

healthy

serving traffic.

unhappy

will serve traffic only if there are no fully healthy tablets.

unhealthy

will not serve traffic.

`

	// binlogTemplate is about the binlog players
	binlogTemplate = `
{{if .Controllers}}
Binlog player state: {{.State}}

    Index
    SourceShard
    StopAtGroupId
    LastGroupId
    Counts
    Rates

  {{range .Controllers}}

      {{.Index}}
      {{.SourceShard.AsHTML}}
      {{if .StopAtGroupId}}{{.StopAtGroupId}}{{end}}
      {{.LastGroupId}}
      {{range $key, $value := .Counts}}{{$key}}: {{$value}}
{{end}}
      {{range $key, $values := .Rates}}{{$key}}: {{range $values}}{{.}} {{end}}
{{end}}

  {{end}}

{{else}}
No binlog player is running.
{{end}}
`
)

type healthStatus struct {
	Records []interface{}
}

func (hs *healthStatus) Current() string {
	if len(hs.Records) > 0 {
		return hs.Records[0].(*tabletmanager.HealthRecord).Class()
	}
	return "unknown"
}

func healthHTMLName() template.HTML {
	return health.HTMLName()
}

func init() {
	servenv.OnRun(func() {
		servenv.AddStatusPart("Tablet", tabletTemplate, func() interface{} {
			return agent.Tablet()
		})
		if agent.IsRunningHealthCheck() {
			servenv.AddStatusFuncs(template.FuncMap{
				"github_com_youtube_vitess_health_html_name": healthHTMLName,
			})
			servenv.AddStatusPart("Health", healthTemplate, func() interface{} {
				return &healthStatus{Records: agent.History.Records()}
			})
		}
		tabletserver.AddStatusPart()
		servenv.AddStatusPart("Binlog Player", binlogTemplate, func() interface{} {
			return agent.BinlogPlayerMap.Status()
		})
	})
}
<|endoftext|>


<commit_before>package main

import "github.com/jpillora/go-ogle-analytics"

func fireEvent( cid, ds, aid string,
	ai, av string,
	eType, eSubType, eId string,
	eState string,
	eLabel string, eVal int64 ) {

	client, err := ga.NewClient("UA-127388617-1")
	if err != nil {
		panic(err)
	}

	//client.ClientID("20eacc6c-e109-11e8-9f32-f2801f1b9fd1")
	client.ClientID( cid )
	client.DataSource( ds )
	client.ApplicationInstallerID( aid )

	client.ApplicationID(ai)
	client.ApplicationVersion(av)

	client.ApplicationName(eSubType)
	client.DocumentTitle(eId)
	event := ga.NewEvent(eType, eState)
	event.Label( eLabel )
	event.Value( eVal )

	err = client.Send(event)
	//err = client.Send(ga.NewEvent("Foo", "Bar").Label("Bazz"))
	if err != nil {
		panic(err)
	}
	println("Event fired!")
}

func main() {

	/**
	//Install Events - Init or Running
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Install", "Linux-AMD64", "20eacc6c-0001-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	fireEvent( "20eacc6c-0002-0000-0000-f2801f1b9fd1", "AWS", "1.10",
			"OpenEBS", "0.8.0",
			"Install", "Linux-AMD64", "20eacc6c-0002-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	fireEvent( "20eacc6c-0003-0000-0000-f2801f1b9fd1", "GKE", "1.9.7",
			"OpenEBS", "0.7.1",
			"Install", "Linux-AMD64", "20eacc6c-0003-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	**/

	/**
	//Install Events - Running
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Install", "Linux-AMD64", "20eacc6c-0001-0000-0000-f2801f1b9fd1",
			"Running",
			"Nodes", 1 )
	**/

	/**
	//Install Events - Init (upgraded minikube openebs version)
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.3",
			"Install", "Linux-AMD64", "20eacc6c-0001-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	**/

	/**
	//Volume Provisioning Events
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Provisioned", "Jiva", "20eacc6c-0001-0001-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 10 )

	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Provisioned", "cStor", "20eacc6c-0001-0002-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 100 )

	//Volume Deprovisioning Events
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Deprovisioned", "Jiva", "20eacc6c-0001-0001-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 10 )

	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Deprovisioned", "cStor", "20eacc6c-0001-0002-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 100 )
	**/
}
<commit_msg>add example to push events to GA #contd<commit_after>package main

import "github.com/jpillora/go-ogle-analytics"

func fireEvent( cid, ds, aid string,
	ai, av string,
	eType, eSubType, eId string,
	eState string,
	eLabel string, eVal int64 ) {

	client, err := ga.NewClient("UA-127388617-1")
	if err != nil {
		panic(err)
	}

	//client.ClientID("20eacc6c-e109-11e8-9f32-f2801f1b9fd1")
	client.ClientID(cid).
		DataSource(ds).
		ApplicationInstallerID( aid ).
		ApplicationID(ai).
		ApplicationVersion(av)

	client.ApplicationName(eSubType).
		DocumentTitle(eId)

	event := ga.NewEvent(eType, eState)
	event.
		Label(eLabel).
		Value(eVal)

	err = client.Send(event)
	//err = client.Send(ga.NewEvent("Foo", "Bar").Label("Bazz"))
	if err != nil {
		panic(err)
	}
	println("Event fired!")
}

func main() {

	/**
	//Install Events - Init or Running
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Install", "Linux-AMD64", "20eacc6c-0001-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	fireEvent( "20eacc6c-0002-0000-0000-f2801f1b9fd1", "AWS", "1.10",
			"OpenEBS", "0.8.0",
			"Install", "Linux-AMD64", "20eacc6c-0002-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	fireEvent( "20eacc6c-0003-0000-0000-f2801f1b9fd1", "GKE", "1.9.7",
			"OpenEBS", "0.7.1",
			"Install", "Linux-AMD64", "20eacc6c-0003-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	**/

	/**
	//Install Events - Running
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Install", "Linux-AMD64", "20eacc6c-0001-0000-0000-f2801f1b9fd1",
			"Running",
			"Nodes", 1 )
	**/

	/**
	//Install Events - Init (upgraded minikube openebs version)
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.3",
			"Install", "Linux-AMD64", "20eacc6c-0001-0000-0000-f2801f1b9fd1",
			"Init",
			"Nodes", 1 )
	**/

	/**
	//Volume Provisioning Events
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Provisioned", "Jiva", "20eacc6c-0001-0001-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 10 )

	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Provisioned", "cStor", "20eacc6c-0001-0002-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 100 )

	//Volume Deprovisioning Events
	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Deprovisioned", "Jiva", "20eacc6c-0001-0001-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 10 )

	fireEvent( "20eacc6c-0001-0000-0000-f2801f1b9fd1", "Minikube", "1.12",
			"OpenEBS", "0.8.2",
			"Volume Deprovisioned", "cStor", "20eacc6c-0001-0002-0000-f2801f1b9fd1",
			"Success",
			"Capacity", 100 )
	**/
}
<|endoftext|>


<commit_before>// Copyright 2019 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package search

import (
	"context"
	"os"
	"path/filepath"
	"sync"

	"github.com/blevesearch/bleve"
	"github.com/blevesearch/bleve/index/store"
	"github.com/blevesearch/bleve/mapping"
	"github.com/blevesearch/bleve/registry"
	"github.com/keybase/client/go/kbfs/data"
	"github.com/keybase/client/go/kbfs/idutil"
	"github.com/keybase/client/go/kbfs/libfs"
	"github.com/keybase/client/go/kbfs/libkbfs"
	"github.com/keybase/client/go/kbfs/tlf"
	kbname "github.com/keybase/client/go/kbun"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/pkg/errors"
)

const (
	textFileType      = "kbfsTextFile"
	kvstoreName       = "kbfs"
	bleveIndexType    = "upside_down"
	fsIndexStorageDir = "kbfs_index"
)

const (
	// CtxOpID is the display name for the unique operation index ID tag.
	ctxOpID = "IID"
)

// CtxTagKey is the type used for unique context tags
type ctxTagKey int

const (
	// CtxIDKey is the type of the tag for unique operation IDs.
	ctxIDKey ctxTagKey = iota
)

// Indexer can index and search KBFS TLFs.
type Indexer struct {
	config       libkbfs.Config
	log          logger.Logger
	cancelLoop   context.CancelFunc
	remoteStatus libfs.RemoteStatus

	userChangedCh chan struct{}

	lock        sync.RWMutex
	index       bleve.Index
	indexConfig libkbfs.Config
}

// NewIndexer creates a new instance of an Indexer.
func NewIndexer(config libkbfs.Config) (*Indexer, error) {
	log := config.MakeLogger("search")
	i := &Indexer{
		config:        config,
		log:           log,
		userChangedCh: make(chan struct{}, 1),
	}

	ctx, cancel := context.WithCancel(i.makeContext(context.Background()))
	i.cancelLoop = cancel
	go i.loop(ctx)
	return i, nil
}

func (i *Indexer) makeContext(ctx context.Context) context.Context {
	return libkbfs.CtxWithRandomIDReplayable(ctx, ctxIDKey, ctxOpID, i.log)
}

func (i *Indexer) closeIndexLocked(ctx context.Context) error {
	if i.index == nil {
		return nil
	}

	err := i.index.Close()
	if err != nil {
		return err
	}
	i.index = nil

	return i.indexConfig.Shutdown(ctx)
}

func (i *Indexer) loadIndex(ctx context.Context) (err error) {
	i.lock.Lock()
	defer i.lock.Unlock()

	err = i.closeIndexLocked(ctx)
	if err != nil {
		return err
	}

	session, err := idutil.GetCurrentSessionIfPossible(
		ctx, i.config.KBPKI(), true)
	if err != nil {
		return err
	}
	if session.Name == "" {
		i.log.CDebugf(ctx, "Not indexing while logged out")
		return nil
	}

	// Create a new Config object for the index data, with a storage
	// root that's unique to this user.
	kbCtx := i.config.KbContext()
	params, err := Params(kbCtx, i.config.StorageRoot(), session.UID)
	if err != nil {
		return err
	}
	ctx, indexConfig, err := Init(
		ctx, kbCtx, params, libkbfs.NewKeybaseServicePassthrough(i.config),
		i.log, i.config.VLogLevel())
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			configErr := indexConfig.Shutdown(ctx)
			if configErr != nil {
				i.log.CDebugf(ctx, "Couldn't shutdown config: %+v", configErr)
			}
		}
	}()

	// Store the index in a KBFS private folder for the current user,
	// with all the blocks and MD stored in the storage root created
	// above.  Everything will be encrypted as if it were in the
	// user's own private KBFS folder.
	privateHandle, err := libkbfs.GetHandleFromFolderNameAndType(
		ctx, indexConfig.KBPKI(), indexConfig.MDOps(), indexConfig,
		string(session.Name), tlf.Private)
	if err != nil {
		return err
	}
	fs, err := libfs.NewFS(
		ctx, indexConfig, privateHandle, data.MasterBranch, "", "", 0)
	if err != nil {
		return err
	}
	err = fs.MkdirAll(fsIndexStorageDir, 0400)
	if err != nil {
		return err
	}
	fs, err = fs.ChrootAsLibFS(fsIndexStorageDir)
	if err != nil {
		return err
	}

	// The index itself will use LevelDB storage that writes to the
	// KBFS filesystem object made above.  Register this storage type
	// with Bleve.
	kvstoreConstructor := func(
		mo store.MergeOperator, _ map[string]interface{}) (
		s store.KVStore, err error) {
		return newBleveLevelDBStore(fs, false, mo)
	}
	registry.RegisterKVStore(kvstoreName, kvstoreConstructor)

	// Create the actual index using this storage type.  Bleve has
	// different calls for new vs. existing indicies, so we first need
	// to check if it exists.  The Bleve LevelDB storage takes a lock,
	// so we don't really need to worry about concurrent KBFS
	// processes here.
	var index bleve.Index
	p := filepath.Join(i.config.StorageRoot(), indexStorageDir, bleveIndexDir)
	_, err = os.Stat(p)
	switch {
	case os.IsNotExist(errors.Cause(err)):
		i.log.CDebugf(ctx, "Creating new index for user %s/%s",
			session.Name, session.UID)

		// TODO: add an html/md document parser to get rid of tags.
		indexMapping := bleve.NewIndexMapping()
		m := mapping.NewDocumentMapping()
		indexMapping.AddDocumentMapping(textFileType, m)
		index, err = bleve.NewUsing(
			p, indexMapping, bleveIndexType, kvstoreName, nil)
		if err != nil {
			return err
		}
	case err == nil:
		i.log.CDebugf(ctx, "Using existing index for user %s/%s",
			session.Name, session.UID)

		index, err = bleve.OpenUsing(p, nil)
		if err != nil {
			return err
		}
	default:
		return err
	}

	i.index = index
	i.indexConfig = indexConfig
	return nil
}

// UserChanged implements the libfs.RemoteStatusUpdater for Indexer.
func (i *Indexer) UserChanged(
	ctx context.Context, oldName, newName kbname.NormalizedUsername) {
	select {
	case i.userChangedCh <- struct{}{}:
	default:
		i.log.CDebugf(ctx, "Dropping user changed notification")
	}
}

var _ libfs.RemoteStatusUpdater = (*Indexer)(nil)

func (i *Indexer) loop(ctx context.Context) {
	i.log.CDebugf(ctx, "Starting indexing loop")
	defer i.log.CDebugf(ctx, "Ending index loop")

	i.remoteStatus.Init(ctx, i.log, i.config, i)

outerLoop:
	for {
		err := i.loadIndex(ctx)
		if err != nil {
			i.log.CDebugf(ctx, "Couldn't load index: %+v", err)
		}

		state := keybase1.MobileAppState_FOREGROUND
		kbCtx := i.config.KbContext()
		for {
			select {
			case <-i.userChangedCh:
				// Re-load the index on each login/logout event.
				i.log.CDebugf(ctx, "User changed")
				continue outerLoop
			case state = <-kbCtx.NextAppStateUpdate(&state):
				// TODO(HOTPOT-1494): once we are doing actual
				// indexing in a separate goroutine, pause/unpause it
				// via a channel send from here.
				for state != keybase1.MobileAppState_FOREGROUND {
					i.log.CDebugf(ctx,
						"Pausing indexing while not foregrounded: state=%s",
						state)
					state = <-kbCtx.NextAppStateUpdate(&state)
				}
				i.log.CDebugf(ctx, "Resuming indexing while foregrounded")
				continue
			case <-ctx.Done():
				return
			}
		}
	}
}

// Shutdown shuts down this indexer.
func (i *Indexer) Shutdown(ctx context.Context) error {
	i.cancelLoop()

	i.lock.Lock()
	defer i.lock.Unlock()

	return i.closeIndexLocked(ctx)
}
<commit_msg>search: register for synced TLF notifications<commit_after>// Copyright 2019 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package search

import (
	"context"
	"os"
	"path/filepath"
	"sync"

	"github.com/blevesearch/bleve"
	"github.com/blevesearch/bleve/index/store"
	"github.com/blevesearch/bleve/mapping"
	"github.com/blevesearch/bleve/registry"
	"github.com/keybase/client/go/kbfs/data"
	"github.com/keybase/client/go/kbfs/idutil"
	"github.com/keybase/client/go/kbfs/kbfsmd"
	"github.com/keybase/client/go/kbfs/libfs"
	"github.com/keybase/client/go/kbfs/libkbfs"
	"github.com/keybase/client/go/kbfs/tlf"
	kbname "github.com/keybase/client/go/kbun"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/pkg/errors"
)

const (
	textFileType      = "kbfsTextFile"
	kvstoreName       = "kbfs"
	bleveIndexType    = "upside_down"
	fsIndexStorageDir = "kbfs_index"
)

const (
	// CtxOpID is the display name for the unique operation index ID tag.
	ctxOpID = "IID"
)

// CtxTagKey is the type used for unique context tags
type ctxTagKey int

const (
	// CtxIDKey is the type of the tag for unique operation IDs.
	ctxIDKey ctxTagKey = iota
)

type tlfMessage struct {
	tlfID tlf.ID
	rev   kbfsmd.Revision
	mode  keybase1.FolderSyncMode
}

// Indexer can index and search KBFS TLFs.
type Indexer struct {
	config       libkbfs.Config
	log          logger.Logger
	cancelLoop   context.CancelFunc
	remoteStatus libfs.RemoteStatus

	userChangedCh chan struct{}
	tlfCh         chan tlfMessage
	shutdownCh    chan struct{}

	lock        sync.RWMutex
	index       bleve.Index
	indexConfig libkbfs.Config
}

// NewIndexer creates a new instance of an Indexer.
func NewIndexer(config libkbfs.Config) (*Indexer, error) {
	log := config.MakeLogger("search")
	i := &Indexer{
		config:        config,
		log:           log,
		userChangedCh: make(chan struct{}, 1),
		tlfCh:         make(chan tlfMessage, 1000),
		shutdownCh:    make(chan struct{}),
	}

	ctx, cancel := context.WithCancel(i.makeContext(context.Background()))
	i.cancelLoop = cancel
	go i.loop(ctx)
	err := config.Notifier().RegisterForSyncedTlfs(i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

func (i *Indexer) makeContext(ctx context.Context) context.Context {
	return libkbfs.CtxWithRandomIDReplayable(ctx, ctxIDKey, ctxOpID, i.log)
}

func (i *Indexer) closeIndexLocked(ctx context.Context) error {
	if i.index == nil {
		return nil
	}

	err := i.index.Close()
	if err != nil {
		return err
	}
	i.index = nil

	return i.indexConfig.Shutdown(ctx)
}

func (i *Indexer) loadIndex(ctx context.Context) (err error) {
	i.lock.Lock()
	defer i.lock.Unlock()

	err = i.closeIndexLocked(ctx)
	if err != nil {
		return err
	}

	session, err := idutil.GetCurrentSessionIfPossible(
		ctx, i.config.KBPKI(), true)
	if err != nil {
		return err
	}
	if session.Name == "" {
		i.log.CDebugf(ctx, "Not indexing while logged out")
		return nil
	}

	// Create a new Config object for the index data, with a storage
	// root that's unique to this user.
	kbCtx := i.config.KbContext()
	params, err := Params(kbCtx, i.config.StorageRoot(), session.UID)
	if err != nil {
		return err
	}
	ctx, indexConfig, err := Init(
		ctx, kbCtx, params, libkbfs.NewKeybaseServicePassthrough(i.config),
		i.log, i.config.VLogLevel())
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			configErr := indexConfig.Shutdown(ctx)
			if configErr != nil {
				i.log.CDebugf(ctx, "Couldn't shutdown config: %+v", configErr)
			}
		}
	}()

	// Store the index in a KBFS private folder for the current user,
	// with all the blocks and MD stored in the storage root created
	// above.  Everything will be encrypted as if it were in the
	// user's own private KBFS folder.
	privateHandle, err := libkbfs.GetHandleFromFolderNameAndType(
		ctx, indexConfig.KBPKI(), indexConfig.MDOps(), indexConfig,
		string(session.Name), tlf.Private)
	if err != nil {
		return err
	}
	fs, err := libfs.NewFS(
		ctx, indexConfig, privateHandle, data.MasterBranch, "", "", 0)
	if err != nil {
		return err
	}
	err = fs.MkdirAll(fsIndexStorageDir, 0400)
	if err != nil {
		return err
	}
	fs, err = fs.ChrootAsLibFS(fsIndexStorageDir)
	if err != nil {
		return err
	}

	// The index itself will use LevelDB storage that writes to the
	// KBFS filesystem object made above.  Register this storage type
	// with Bleve.
	kvstoreConstructor := func(
		mo store.MergeOperator, _ map[string]interface{}) (
		s store.KVStore, err error) {
		return newBleveLevelDBStore(fs, false, mo)
	}
	registry.RegisterKVStore(kvstoreName, kvstoreConstructor)

	// Create the actual index using this storage type.  Bleve has
	// different calls for new vs. existing indicies, so we first need
	// to check if it exists.  The Bleve LevelDB storage takes a lock,
	// so we don't really need to worry about concurrent KBFS
	// processes here.
	var index bleve.Index
	p := filepath.Join(i.config.StorageRoot(), indexStorageDir, bleveIndexDir)
	_, err = os.Stat(p)
	switch {
	case os.IsNotExist(errors.Cause(err)):
		i.log.CDebugf(ctx, "Creating new index for user %s/%s",
			session.Name, session.UID)

		// TODO: add an html/md document parser to get rid of tags.
		indexMapping := bleve.NewIndexMapping()
		m := mapping.NewDocumentMapping()
		indexMapping.AddDocumentMapping(textFileType, m)
		index, err = bleve.NewUsing(
			p, indexMapping, bleveIndexType, kvstoreName, nil)
		if err != nil {
			return err
		}
	case err == nil:
		i.log.CDebugf(ctx, "Using existing index for user %s/%s",
			session.Name, session.UID)

		index, err = bleve.OpenUsing(p, nil)
		if err != nil {
			return err
		}
	default:
		return err
	}

	i.index = index
	i.indexConfig = indexConfig
	return nil
}

// UserChanged implements the libfs.RemoteStatusUpdater for Indexer.
func (i *Indexer) UserChanged(
	ctx context.Context, oldName, newName kbname.NormalizedUsername) {
	select {
	case i.userChangedCh <- struct{}{}:
	default:
		i.log.CDebugf(ctx, "Dropping user changed notification")
	}
}

var _ libfs.RemoteStatusUpdater = (*Indexer)(nil)

// FullSyncStarted implements the libkbfs.SyncedTlfObserver interface
// for Indexer.
func (i *Indexer) FullSyncStarted(
	ctx context.Context, tlfID tlf.ID, rev kbfsmd.Revision,
	waitCh <-chan struct{}) {
	i.log.CDebugf(ctx, "Sync started for %s/%d", tlfID, rev)
	go func() {
		select {
		case <-waitCh:
		case <-i.shutdownCh:
			return
		}

		m := tlfMessage{tlfID, rev, keybase1.FolderSyncMode_ENABLED}
		select {
		case i.tlfCh <- m:
		default:
			i.log.CDebugf(
				context.Background(), "Couldn't send TLF message for %s/%d")
		}
	}()
}

// SyncModeChanged implements the libkbfs.SyncedTlfObserver interface
// for Indexer.
func (i *Indexer) SyncModeChanged(
	ctx context.Context, tlfID tlf.ID, newMode keybase1.FolderSyncMode) {
	i.log.CDebugf(ctx, "Sync mode changed for %s to %s", tlfID, newMode)
	m := tlfMessage{tlfID, kbfsmd.RevisionUninitialized, newMode}
	select {
	case i.tlfCh <- m:
	default:
		i.log.CDebugf(
			context.Background(), "Couldn't send TLF message for %s/%d")
	}
}

var _ libkbfs.SyncedTlfObserver = (*Indexer)(nil)

func (i *Indexer) loop(ctx context.Context) {
	i.log.CDebugf(ctx, "Starting indexing loop")
	defer i.log.CDebugf(ctx, "Ending index loop")

	i.remoteStatus.Init(ctx, i.log, i.config, i)

outerLoop:
	for {
		err := i.loadIndex(ctx)
		if err != nil {
			i.log.CDebugf(ctx, "Couldn't load index: %+v", err)
		}

		state := keybase1.MobileAppState_FOREGROUND
		kbCtx := i.config.KbContext()
		for {
			select {
			case <-i.userChangedCh:
				// Re-load the index on each login/logout event.
				i.log.CDebugf(ctx, "User changed")
				continue outerLoop
			case state = <-kbCtx.NextAppStateUpdate(&state):
				// TODO(HOTPOT-1494): once we are doing actual
				// indexing in a separate goroutine, pause/unpause it
				// via a channel send from here.
				for state != keybase1.MobileAppState_FOREGROUND {
					i.log.CDebugf(ctx,
						"Pausing indexing while not foregrounded: state=%s",
						state)
					state = <-kbCtx.NextAppStateUpdate(&state)
				}
				i.log.CDebugf(ctx, "Resuming indexing while foregrounded")
				continue
			case m := <-i.tlfCh:
				i.log.CDebugf(ctx, "Received TLF message for %s", m.tlfID)
				// TODO(HOTPOT-1494, HOTPOT-1495): initiate processing pass.
			case <-ctx.Done():
				return
			}
		}
	}
}

// Shutdown shuts down this indexer.
func (i *Indexer) Shutdown(ctx context.Context) error {
	i.cancelLoop()
	close(i.shutdownCh)

	i.lock.Lock()
	defer i.lock.Unlock()

	return i.closeIndexLocked(ctx)
}
<|endoftext|>


<commit_before>package gofakes3

import (
	"encoding/xml"
	"fmt"
	"net/http/httptest"
	"testing"
)

func TestHttpError(t *testing.T) {
	var g GoFakeS3
	rq := httptest.NewRequest("GET", "/", nil)
	rs := httptest.NewRecorder()
	g.httpError(rs, rq, ErrNoSuchBucket)
	if rs.Code != 404 {
		t.Fatal()
	}
	if rs.Body.Len() == 0 {
		t.Fatal()
	}
	var resp ErrorResponse
	if err := xml.Unmarshal(rs.Body.Bytes(), &resp); err != nil {
		t.Fatal(err)
	}

	if resp.Code != ErrNoSuchBucket {
		t.Fatal()
	}
}

func TestHttpErrorWriteFailure(t *testing.T) {
	// FIXME: with a pluggable logger, we can intercept the log message to
	// verify the write error is handled.
	var g GoFakeS3
	rq := httptest.NewRequest("GET", "/", nil)
	rs := httptest.NewRecorder()
	g.httpError(&failingResponseWriter{rs}, rq, ErrNoSuchBucket)
	if rs.Code != 404 {
		t.Fatal()
	}
	if rs.Body.Len() != 0 {
		t.Fatal()
	}
}

type failingResponseWriter struct {
	*httptest.ResponseRecorder
}

func (w *failingResponseWriter) Write(buf []byte) (n int, err error) {
	return 0, fmt.Errorf("nope")
}
<commit_msg>Add logger to HTTP write error test<commit_after>package gofakes3

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log"
	"net/http/httptest"
	"testing"
)

func TestHttpError(t *testing.T) {
	var g GoFakeS3
	rq := httptest.NewRequest("GET", "/", nil)
	rs := httptest.NewRecorder()
	g.httpError(rs, rq, ErrNoSuchBucket)
	if rs.Code != 404 {
		t.Fatal()
	}
	if rs.Body.Len() == 0 {
		t.Fatal()
	}
	var resp ErrorResponse
	if err := xml.Unmarshal(rs.Body.Bytes(), &resp); err != nil {
		t.Fatal(err)
	}

	if resp.Code != ErrNoSuchBucket {
		t.Fatal()
	}
}

func TestHttpErrorWriteFailure(t *testing.T) {
	var buf bytes.Buffer
	std := log.New(&buf, "", 0)
	logger := StdLog(std)
	var g = GoFakeS3{
		log: logger,
	}

	rq := httptest.NewRequest("GET", "/", nil)
	rs := httptest.NewRecorder()
	g.httpError(&failingResponseWriter{rs}, rq, ErrNoSuchBucket)
	if rs.Code != 404 {
		t.Fatal()
	}
	if rs.Body.Len() != 0 {
		t.Fatal()
	}
	if buf.String() != "ERR nope\n" {
		t.Fatal()
	}
}

type failingResponseWriter struct {
	*httptest.ResponseRecorder
}

func (w *failingResponseWriter) Write(buf []byte) (n int, err error) {
	return 0, fmt.Errorf("nope")
}
<|endoftext|>


<commit_before>// +build !windows

package runner

import (
	"fmt"
	"os/exec"
	"syscall"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type UserCredential *syscall.Credential

func setUserCredential(runner *GardenRunner) {
	credential := runner.User
	if runner.Socket2meSocketPath != "" {
		// socket2me sets uid/gid
		credential = nil
	}

	runner.Command.SysProcAttr = &syscall.SysProcAttr{Credential: credential}
}

func socket2meCommand(config GdnRunnerConfig) *exec.Cmd {
	return exec.Command(
		config.Socket2meBin,
		append(
			[]string{
				"--socket-path",
				config.Socket2meSocketPath,
				fmt.Sprintf("--uid=%d", config.User.Uid),
				fmt.Sprintf("--gid=%d", config.User.Gid),
				"--socket-uid=0", "--socket-gid=0",
				config.GdnBin,
			},
			config.toServerFlags()...,
		)...,
	)
}

func (r *GardenRunner) setupDirsForUser() {
	if r.User != nil {
		uidGid := fmt.Sprintf("%d:%d", r.User.Uid, r.User.Gid)

		cmd := exec.Command("chown", uidGid, r.TmpDir)
		cmd.Stdout = GinkgoWriter
		cmd.Stderr = GinkgoWriter
		Expect(cmd.Run()).To(Succeed())

		Eventually(func() error {
			cmd := exec.Command("chown", "-R", uidGid, r.DepotDir)
			cmd.Stdout = GinkgoWriter
			cmd.Stderr = GinkgoWriter
			return cmd.Run()
		}, "3s", "1s").Should(Succeed())
	}
}

func (r *RunningGarden) Cleanup() error {
	// GROOT CLEANUP
	storePath := r.GardenRunner.GdnRunnerConfig.StorePath
	privStorePath := r.GardenRunner.GdnRunnerConfig.PrivilegedStorePath

	if err := clearGrootStore(r.GrootBin, storePath); err != nil {
		return err
	}
	if err := clearGrootStore(r.GrootBin, privStorePath); err != nil {
		return err
	}

	return nil
}

func clearGrootStore(grootBin, storePath string) error {
	deleteStoreArgs := []string{"--store", storePath, "delete-store"}

	deleteStore := exec.Command(grootBin, deleteStoreArgs...)
	deleteStore.Stdout = GinkgoWriter
	deleteStore.Stderr = GinkgoWriter
	return deleteStore.Run()
}
<commit_msg>List opened files in store on cleanup failure<commit_after>// +build !windows

package runner

import (
	"fmt"
	"os/exec"
	"syscall"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type UserCredential *syscall.Credential

func setUserCredential(runner *GardenRunner) {
	credential := runner.User
	if runner.Socket2meSocketPath != "" {
		// socket2me sets uid/gid
		credential = nil
	}

	runner.Command.SysProcAttr = &syscall.SysProcAttr{Credential: credential}
}

func socket2meCommand(config GdnRunnerConfig) *exec.Cmd {
	return exec.Command(
		config.Socket2meBin,
		append(
			[]string{
				"--socket-path",
				config.Socket2meSocketPath,
				fmt.Sprintf("--uid=%d", config.User.Uid),
				fmt.Sprintf("--gid=%d", config.User.Gid),
				"--socket-uid=0", "--socket-gid=0",
				config.GdnBin,
			},
			config.toServerFlags()...,
		)...,
	)
}

func (r *GardenRunner) setupDirsForUser() {
	if r.User != nil {
		uidGid := fmt.Sprintf("%d:%d", r.User.Uid, r.User.Gid)

		cmd := exec.Command("chown", uidGid, r.TmpDir)
		cmd.Stdout = GinkgoWriter
		cmd.Stderr = GinkgoWriter
		Expect(cmd.Run()).To(Succeed())

		Eventually(func() error {
			cmd := exec.Command("chown", "-R", uidGid, r.DepotDir)
			cmd.Stdout = GinkgoWriter
			cmd.Stderr = GinkgoWriter
			return cmd.Run()
		}, "3s", "1s").Should(Succeed())
	}
}

func (r *RunningGarden) Cleanup() error {
	// GROOT CLEANUP
	storePath := r.GardenRunner.GdnRunnerConfig.StorePath
	privStorePath := r.GardenRunner.GdnRunnerConfig.PrivilegedStorePath

	if err := clearGrootStore(r.GrootBin, storePath); err != nil {
		return err
	}
	if err := clearGrootStore(r.GrootBin, privStorePath); err != nil {
		return err
	}

	return nil
}

func clearGrootStore(grootBin, storePath string) error {
	deleteStoreArgs := []string{"--store", storePath, "delete-store"}

	// Ignore lsof errors as lsof would return exit code 1 in case it failed, OR did not find any opened files
	lsofOutput, _ := exec.Command("lsof", "-V", storePath).CombinedOutput()

	deleteStore := exec.Command(grootBin, deleteStoreArgs...)
	deleteStore.Stdout = GinkgoWriter
	deleteStore.Stderr = GinkgoWriter
	if err := deleteStore.Run(); err != nil {
		return fmt.Errorf("Delete store failed with %s, lsof(%s) output was %s", err.Error(), storePath, string(lsofOutput))
	}
	return nil
}
<|endoftext|>


<commit_before>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	shellPath, err := pathToSkiffCore()
	if err != nil {
		return err
	}
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-G",
			"docker",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	} else {
		// Set the shell for the user
		le.WithField("path", shellPath).Debug("Setting shell")
		if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
			return err
		}

		// Add to the Docker group
		/*
			le.WithField("path", shellPath).Debug("Adding to Docker group")
			if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
				return err
			}
		*/
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\n%s\n", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.MkdirAll(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	authConf := cs.config.Auth
	if authConf != nil {
		if authConf.CopyRootKeys {
			rkf, err := os.Open("/root/.ssh/authorized_keys")
			if err != nil {
				return err
			}
			_, err = io.Copy(f, rkf)
			rkf.Close()
			if err != nil {
				return err
			}
			f.WriteString("\n")
		}
		for _, key := range authConf.SSHKeys {
			f.WriteString(key)
			f.WriteString("\n")
		}
	}

	f.Sync()
	if err := os.Chown(authorizedKeysPath, uid, gid); err != nil {
		return err
	}

	containerId, err := cs.waiter.WaitForContainer(cs.config.Container)
	if err != nil {
		return err
	}

	userConfPath := path.Join(euser.HomeDir, config.UserConfigFile)
	le.WithField("path", userConfPath).Debug("Writing user config...")
	userConf := cs.config.ToConfigUserShell(containerId)
	userConfData, err := userConf.Marshal()
	if err != nil {
		return err
	}

	userConfFile, err := os.OpenFile(userConfPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	if _, err := userConfFile.Write(userConfData); err != nil {
		return err
	}
	userConfFile.Close()

	return os.Chown(userConfPath, uid, gid)
}

// Wait waits for Execute() to finish.
func (i *UserSetup) Wait() error {
	i.wg.Wait()
	return i.err
}
<commit_msg>fix: create user home dir if not exist<commit_after>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	shellPath, err := pathToSkiffCore()
	if err != nil {
		return err
	}
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-G",
			"docker",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	} else {
		// Set the shell for the user
		le.WithField("path", shellPath).Debug("Setting shell")
		if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
			return err
		}

		// Add to the Docker group
		/*
			le.WithField("path", shellPath).Debug("Adding to Docker group")
			if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
				return err
			}
		*/
	}

	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\n%s\n", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(euser.HomeDir); os.IsNotExist(err) {
		if err := os.MkdirAll(euser.HomeDir, 0755); err != nil {
			return err
		}
		if err := os.Chown(euser.HomeDir, uid, gid); err != nil {
			return err
		}
	}
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.MkdirAll(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	authConf := cs.config.Auth
	if authConf != nil {
		if authConf.CopyRootKeys {
			rkf, err := os.Open("/root/.ssh/authorized_keys")
			if err != nil {
				return err
			}
			_, err = io.Copy(f, rkf)
			rkf.Close()
			if err != nil {
				return err
			}
			f.WriteString("\n")
		}
		for _, key := range authConf.SSHKeys {
			f.WriteString(key)
			f.WriteString("\n")
		}
	}

	f.Sync()
	if err := os.Chown(authorizedKeysPath, uid, gid); err != nil {
		return err
	}

	containerId, err := cs.waiter.WaitForContainer(cs.config.Container)
	if err != nil {
		return err
	}

	userConfPath := path.Join(euser.HomeDir, config.UserConfigFile)
	le.WithField("path", userConfPath).Debug("Writing user config...")
	userConf := cs.config.ToConfigUserShell(containerId)
	userConfData, err := userConf.Marshal()
	if err != nil {
		return err
	}

	userConfFile, err := os.OpenFile(userConfPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	if _, err := userConfFile.Write(userConfData); err != nil {
		return err
	}
	userConfFile.Close()

	return os.Chown(userConfPath, uid, gid)
}

// Wait waits for Execute() to finish.
func (i *UserSetup) Wait() error {
	i.wg.Wait()
	return i.err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"testing"
)

func TestShardIndex(t *testing.T) {
	idx := newShardIndex()

	// Test empty
	if idx.Test("test") == true {
		t.Error("Should not be in here")
	}

	// Add
	idx.Add("test")

	// Test contains
	if idx.Test("test") == false {
		t.Error("Should be in here")
	}

	// Convert to bytes
	idxB := idx.Bytes()
	if len(idxB) < 1024 {
		t.Error("Should be of substantial size")
	}

	// New empty filter
	idx2 := newShardIndex()
	if idx2.Test("test") == true {
		t.Error("Empty filter must be empty")
	}

	// Populate from bytes
	idx2.FromBytes(idxB)

	// Should now be in there
	if idx2.Test("test") == false {
		t.Error("Empty filter must be populated after loading bytes")
	}
}

func BenchmarkShardIndexWrite(b *testing.B) {
	idx := newShardIndex()
	for n := 0; n < b.N; n++ {
		idx.Add(fmt.Sprintf("%d", n))
	}
}
<commit_msg>Benchmark index read<commit_after>package main

import (
	"fmt"
	"testing"
)

func TestShardIndex(t *testing.T) {
	idx := newShardIndex()

	// Test empty
	if idx.Test("test") == true {
		t.Error("Should not be in here")
	}

	// Add
	idx.Add("test")

	// Test contains
	if idx.Test("test") == false {
		t.Error("Should be in here")
	}

	// Convert to bytes
	idxB := idx.Bytes()
	if len(idxB) < 1024 {
		t.Error("Should be of substantial size")
	}

	// New empty filter
	idx2 := newShardIndex()
	if idx2.Test("test") == true {
		t.Error("Empty filter must be empty")
	}

	// Populate from bytes
	idx2.FromBytes(idxB)

	// Should now be in there
	if idx2.Test("test") == false {
		t.Error("Empty filter must be populated after loading bytes")
	}
}

func BenchmarkShardIndexWrite(b *testing.B) {
	idx := newShardIndex()
	for n := 0; n < b.N; n++ {
		idx.Add(fmt.Sprintf("%d", n))
	}
}

func BenchmarkShardIndexRead(b *testing.B) {
	idx := newShardIndex()
	idx.Add("asdf")
	for n := 0; n < b.N; n++ {
		if n%3 == 0 {
			idx.Test("asdf")
		} else {
			idx.Test("not")
		}
	}
}
<|endoftext|>