<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/elsonwu/goio"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"github.com/zenazn/goji/web/middleware"
)

var flagAllowOrigin = flag.String("alloworigin", "", "the host allow to cross site ajax")
var flagDebug = flag.Bool("debug", false, "enable debug mode or not")
var flagClientLifeCycle = flag.Int64("lifecycle", 30, "how many seconds of the client life cycle")
var flagClientMessageTimeout = flag.Int64("messagetimeout", 15, "how many seconds of the client keep waiting for new messages")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()
	goio.Debug = *flagDebug
	goio.LifeCycle = *flagClientLifeCycle

	clients := goio.GlobalClients()
	rooms := goio.GlobalRooms()
	users := goio.GlobalUsers()

	if *flagDebug {
		go func() {
			for {
				time.Sleep(3 * time.Second)
				log.Printf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())
			}
		}()
	}

	goji.Abandon(middleware.RequestID)
	goji.Abandon(middleware.Logger)
	goji.Use(func(h http.Handler) http.Handler {
		fn := func(res http.ResponseWriter, r *http.Request) {
			res.Header().Set("Content-Type", "text/plain; charset=utf-8")
			res.Header().Set("Access-Control-Allow-Credentials", "true")
			res.Header().Set("Access-Control-Allow-Methods", "GET,POST")
			if "" != *flagAllowOrigin {
				allowOrigins := strings.Split(*flagAllowOrigin, ",")
				for _, allowOrigin := range allowOrigins {
					res.Header().Add("Access-Control-Allow-Origin", allowOrigin)
				}
			}
			res.Header().Set("Content-Type", "text/plain")
			h.ServeHTTP(res, r)
		}

		return http.HandlerFunc(fn)
	})

	if *flagDebug {
		goji.Get("/test", func(w http.ResponseWriter, req *http.Request) {
			st := time.Now().Unix()
			for i := 0; i < 10000; i++ {
				userId := strconv.Itoa(i)
				user := users.Get(userId)
				if user == nil {
					user = goio.NewUser(userId)
				}

				clt, done := goio.NewClient()
				user.Add(clt)
				done <- true

				room := rooms.Get(strconv.Itoa(i%1000), true)
				room.Add(user)
			}

			io.WriteString(w, strconv.Itoa(int(time.Now().Unix()-st))+" seconds")
		})
	}

	goji.Get("/count", func(w http.ResponseWriter, req *http.Request) {
		res := ""
		res += fmt.Sprintf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())

		if "1" == req.URL.Query().Get("detail") {
			res += fmt.Sprintf("-------------------------------\n")

			rooms.Each(func(room *goio.Room) {
				res += fmt.Sprintf("# room id: %s \n", room.Id)
				for userId, _ := range room.UserIds.Map {
					res += fmt.Sprintf(" - user id: %s \n", userId)
				}
				res += fmt.Sprintf("\n")
			})

			res += fmt.Sprintf("-------------------------------\n")

			users.Each(func(user *goio.User) {
				res += fmt.Sprintf("# user id: %s \n", user.Id)
				for clientId, _ := range user.ClientIds.Map {
					res += fmt.Sprintf(" - client id: %s \n", clientId)
				}
				res += fmt.Sprintf("\n")
			})
		}

		io.WriteString(w, res)
	})

	goji.Get("/room/users/:room_id", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		roomId := ctx.URLParams["room_id"]
		if roomId == "" {
			http.Error(w, "room_id is missing", http.StatusForbidden)
			return
		}

		room := rooms.Get(roomId, false)
		if room == nil {
			io.WriteString(w, "room does not exist")
			return
		}

		io.WriteString(w, strings.Join(room.UserIds.Array(), ","))
	})

	goji.Get("/room/users/:room_id", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		roomId := ctx.URLParams["room_id"]
		if roomId == "" {
			http.Error(w, "room_id is missing", http.StatusForbidden)
			return
		}

		room := rooms.Get(roomId, false)
		if room == nil {
			io.WriteString(w, "")
			return
		}

		io.WriteString(w, strings.Join(room.UserIds.Array(), ","))
	})

	goji.Get("/user/data/:user_id/:key", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		userId := ctx.URLParams["user_id"]
		if userId == "" {
			http.Error(w, "user_id is missing", http.StatusForbidden)
			return
		}

		key := ctx.URLParams["key"]
		if key == "" {
			http.Error(w, "key is missing", http.StatusForbidden)
			return
		}

		user := users.Get(userId)
		if user == nil {
			io.WriteString(w, "")
			return
		}

		io.WriteString(w, user.Data().Get(key))
	})

	goji.Post("/user/data/:client_id/:key", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusForbidden)
			return
		}

		clientId := ctx.URLParams["client_id"]
		if clientId == "" {
			http.Error(w, "client_id is missing", http.StatusForbidden)
			return
		}

		key := ctx.URLParams["key"]
		if key == "" {
			http.Error(w, "key is missing", http.StatusForbidden)
			return
		}

		clt := clients.Get(clientId)
		if clt != nil && clt.UserId != "" {
			user := users.Get(clt.UserId)
			if user != nil {
				user.Data().Set(key, string(val))
			}
		}

		io.WriteString(w, "")
	})

	goji.Post("/online_status", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusForbidden)
			return
		}

		userIds := strings.Split(string(val), ",")
		status := ""
		for _, userId := range userIds {
			if nil == users.Get(userId) {
				status += "0,"
			} else {
				status += "1,"
			}
		}

		io.WriteString(w, status)
	})

	goji.Post("/client/:user_id", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		userId := ctx.URLParams["user_id"]
		user := users.Get(userId)
		if user == nil {
			user = goio.NewUser(userId)
		}

		clt, done := goio.NewClient()
		user.Add(clt)
		done <- true

		io.WriteString(w, clt.Id)
	})

	goji.Get("/kill_client/:client_id", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		id := ctx.URLParams["client_id"]
		clt := clients.Get(id)
		if clt != nil {
			clt.Destroy()
		}

		io.WriteString(w, "")
	})

	goji.Get("/message/:client_id", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		id := ctx.URLParams["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			http.Error(w, fmt.Sprintf("Client %s does not exist\n", id), http.StatusNotFound)
			return
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()

		timeNow := time.Now().Unix()
		for {
			if 0 < len(clt.Messages) {
				msgs, err := json.Marshal(clt.Messages)
				if err != nil {
					http.Error(w, err.Error(), http.StatusInternalServerError)
					return
				}

				clt.CleanMessages()
				io.WriteString(w, string(msgs))
				return
			}

			if time.Now().Unix() > timeNow+*flagClientMessageTimeout {
				break
			}

			time.Sleep(100000 * time.Microsecond)
		}

		io.WriteString(w, "")
	})

	goji.Post("/message/:client_id", func(ctx web.C, w http.ResponseWriter, req *http.Request) {
		id := ctx.URLParams["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			http.Error(w, fmt.Sprintf("Client %s does not exist\n", id), http.StatusForbidden)
			return
		}

		user := users.Get(clt.UserId)
		if user == nil {
			http.Error(w, fmt.Sprintf("Client %s does not connect with any user\n", id), http.StatusForbidden)
			return
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()
		defer req.Body.Close()

		message := &goio.Message{}
		json.NewDecoder(req.Body).Decode(message)
		message.CallerId = clt.UserId
		go func(message *goio.Message) {
			if *flagDebug {
				log.Printf("post message: %#v", *message)
			}

			if message.RoomId == "" && (message.EventName == "join" || message.EventName == "leave") {
				clt.Receive(&goio.Message{
					EventName: "error",
					Data:      "room id is missing",
				})

				return
			}

			// We change CallerId as current user
			message.CallerId = user.Id
			message.ClientId = clt.Id
			user.Emit(message.EventName, message)
		}(message)

		io.WriteString(w, "")
	})

	goji.Serve()
}
<commit_msg>rename some variables<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/elsonwu/goio"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"github.com/zenazn/goji/web/middleware"
)

var flagAllowOrigin = flag.String("alloworigin", "", "the host allow to cross site ajax")
var flagDebug = flag.Bool("debug", false, "enable debug mode or not")
var flagClientLifeCycle = flag.Int64("lifecycle", 30, "how many seconds of the client life cycle")
var flagClientMessageTimeout = flag.Int64("messagetimeout", 15, "how many seconds of the client keep waiting for new messages")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()
	goio.Debug = *flagDebug
	goio.LifeCycle = *flagClientLifeCycle

	clients := goio.GlobalClients()
	rooms := goio.GlobalRooms()
	users := goio.GlobalUsers()

	if *flagDebug {
		go func() {
			for {
				time.Sleep(3 * time.Second)
				log.Printf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())
			}
		}()
	}

	goji.Abandon(middleware.RequestID)
	goji.Abandon(middleware.Logger)
	goji.Use(func(h http.Handler) http.Handler {
		fn := func(res http.ResponseWriter, req *http.Request) {
			res.Header().Set("Content-Type", "text/plain; charset=utf-8")
			res.Header().Set("Access-Control-Allow-Credentials", "true")
			res.Header().Set("Access-Control-Allow-Methods", "GET,POST")
			if "" != *flagAllowOrigin {
				allowOrigins := strings.Split(*flagAllowOrigin, ",")
				for _, allowOrigin := range allowOrigins {
					res.Header().Add("Access-Control-Allow-Origin", allowOrigin)
				}
			}
			res.Header().Set("Content-Type", "text/plain")
			h.ServeHTTP(res, req)
		}

		return http.HandlerFunc(fn)
	})

	if *flagDebug {
		goji.Get("/test", func(res http.ResponseWriter, req *http.Request) {
			st := time.Now().Unix()
			for i := 0; i < 10000; i++ {
				userId := strconv.Itoa(i)
				user := users.Get(userId)
				if user == nil {
					user = goio.NewUser(userId)
				}

				clt, done := goio.NewClient()
				user.Add(clt)
				done <- true

				room := rooms.Get(strconv.Itoa(i%1000), true)
				room.Add(user)
			}

			io.WriteString(res, strconv.Itoa(int(time.Now().Unix()-st))+" seconds")
		})
	}

	goji.Get("/count", func(res http.ResponseWriter, req *http.Request) {
		str := ""
		str += fmt.Sprintf("rooms: %d, users: %d, clients: %d \n", rooms.Count(), users.Count(), clients.Count())

		if "1" == req.URL.Query().Get("detail") {
			str += fmt.Sprintf("-------------------------------\n")

			rooms.Each(func(room *goio.Room) {
				str += fmt.Sprintf("# room id: %s \n", room.Id)
				for userId, _ := range room.UserIds.Map {
					str += fmt.Sprintf(" - user id: %s \n", userId)
				}
				str += fmt.Sprintf("\n")
			})

			str += fmt.Sprintf("-------------------------------\n")

			users.Each(func(user *goio.User) {
				str += fmt.Sprintf("# user id: %s \n", user.Id)
				for clientId, _ := range user.ClientIds.Map {
					str += fmt.Sprintf(" - client id: %s \n", clientId)
				}
				str += fmt.Sprintf("\n")
			})
		}

		io.WriteString(res, str)
	})

	goji.Get("/room/users/:room_id", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		roomId := ctx.URLParams["room_id"]
		if roomId == "" {
			http.Error(res, "room_id is missing", http.StatusForbidden)
			return
		}

		room := rooms.Get(roomId, false)
		if room == nil {
			io.WriteString(res, "room does not exist")
			return
		}

		io.WriteString(res, strings.Join(room.UserIds.Array(), ","))
	})

	goji.Get("/room/users/:room_id", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		roomId := ctx.URLParams["room_id"]
		if roomId == "" {
			http.Error(res, "room_id is missing", http.StatusForbidden)
			return
		}

		room := rooms.Get(roomId, false)
		if room == nil {
			io.WriteString(res, "")
			return
		}

		io.WriteString(res, strings.Join(room.UserIds.Array(), ","))
	})

	goji.Get("/user/data/:user_id/:key", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		userId := ctx.URLParams["user_id"]
		if userId == "" {
			http.Error(res, "user_id is missing", http.StatusForbidden)
			return
		}

		key := ctx.URLParams["key"]
		if key == "" {
			http.Error(res, "key is missing", http.StatusForbidden)
			return
		}

		user := users.Get(userId)
		if user == nil {
			io.WriteString(res, "")
			return
		}

		io.WriteString(res, user.Data().Get(key))
	})

	goji.Post("/user/data/:client_id/:key", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			http.Error(res, err.Error(), http.StatusForbidden)
			return
		}

		clientId := ctx.URLParams["client_id"]
		if clientId == "" {
			http.Error(res, "client_id is missing", http.StatusForbidden)
			return
		}

		key := ctx.URLParams["key"]
		if key == "" {
			http.Error(res, "key is missing", http.StatusForbidden)
			return
		}

		clt := clients.Get(clientId)
		if clt != nil && clt.UserId != "" {
			user := users.Get(clt.UserId)
			if user != nil {
				user.Data().Set(key, string(val))
			}
		}

		io.WriteString(res, "")
	})

	goji.Post("/online_status", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		val, err := ioutil.ReadAll(req.Body)
		if err != nil {
			http.Error(res, err.Error(), http.StatusForbidden)
			return
		}

		userIds := strings.Split(string(val), ",")
		status := ""
		for _, userId := range userIds {
			if nil == users.Get(userId) {
				status += "0,"
			} else {
				status += "1,"
			}
		}

		io.WriteString(res, status)
	})

	goji.Post("/client/:user_id", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		userId := ctx.URLParams["user_id"]
		user := users.Get(userId)
		if user == nil {
			user = goio.NewUser(userId)
		}

		clt, done := goio.NewClient()
		user.Add(clt)
		done <- true

		io.WriteString(res, clt.Id)
	})

	goji.Get("/kill_client/:client_id", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		id := ctx.URLParams["client_id"]
		clt := clients.Get(id)
		if clt != nil {
			clt.Destroy()
		}

		io.WriteString(res, "")
	})

	goji.Get("/message/:client_id", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		id := ctx.URLParams["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			http.Error(res, fmt.Sprintf("Client %s does not exist\n", id), http.StatusNotFound)
			return
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()

		timeNow := time.Now().Unix()
		for {
			if 0 < len(clt.Messages) {
				msgs, err := json.Marshal(clt.Messages)
				if err != nil {
					http.Error(res, err.Error(), http.StatusInternalServerError)
					return
				}

				clt.CleanMessages()
				io.WriteString(res, string(msgs))
				return
			}

			if time.Now().Unix() > timeNow+*flagClientMessageTimeout {
				break
			}

			time.Sleep(100000 * time.Microsecond)
		}

		io.WriteString(res, "")
	})

	goji.Post("/message/:client_id", func(ctx web.C, res http.ResponseWriter, req *http.Request) {
		id := ctx.URLParams["client_id"]
		clt := clients.Get(id)
		if clt == nil {
			http.Error(res, fmt.Sprintf("Client %s does not exist\n", id), http.StatusForbidden)
			return
		}

		user := users.Get(clt.UserId)
		if user == nil {
			http.Error(res, fmt.Sprintf("Client %s does not connect with any user\n", id), http.StatusForbidden)
			return
		}

		clt.Handshake()
		// we handshake again after it finished no matter timeout or ok
		defer clt.Handshake()
		defer req.Body.Close()

		message := &goio.Message{}
		json.NewDecoder(req.Body).Decode(message)
		message.CallerId = clt.UserId
		go func(message *goio.Message) {
			if *flagDebug {
				log.Printf("post message: %#v", *message)
			}

			if message.RoomId == "" && (message.EventName == "join" || message.EventName == "leave") {
				clt.Receive(&goio.Message{
					EventName: "error",
					Data:      "room id is missing",
				})

				return
			}

			// We change CallerId as current user
			message.CallerId = user.Id
			message.ClientId = clt.Id
			user.Emit(message.EventName, message)
		}(message)

		io.WriteString(res, "")
	})

	goji.Serve()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"

	"github.com/4406arthur/blog-daddy/adapters/git"
	"github.com/4406arthur/blog-daddy/providers/s3"
	"gopkg.in/russross/blackfriday.v2"

	"log"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

type (
	Article struct {
		Name    string
		ModTime string
	}
)

type MarkdownParser struct {
	reader io.Reader
}

func NewMarkdownParser(r io.Reader) *MarkdownParser {
	return &MarkdownParser{r}
}

func (m *MarkdownParser) Read(p []byte) (n int, err error) {
	n, err = m.reader.Read(p)
	copy(p, blackfriday.Run(p))

	return n, err
}

func main() {
	viper.SetConfigName("config")
	viper.AddConfigPath(".")
	//viper.AddConfigPath("/var/run/secret")
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		panic(err)
	}

	router := gin.Default()
	router.LoadHTMLGlob("templates/*")

	router.POST("/setup", func(c *gin.Context) {
		var u git.User
		if c.BindJSON(&u) == nil {
			//minio cannot create bucket name with uppercase
			//and github is not case sensitive
			//trans to lowercase is not dangerous
			u.User = strings.ToLower(u.User)
			GithubHandler := git.NewGitHandler("https://api.github.com")
			mds := make([]git.GitRepoContent, 0)
			mds, err := GithubHandler.FetchRepoContents(u)
			if err != nil {
				panic(err)
			}
			log.Printf("You have %s\n", mds)

			resp, _ := http.Get(mds[0].Download_Url)
			defer resp.Body.Close()

			m := NewMarkdownParser(resp.Body)

			html, _ := ioutil.ReadAll(m)

			fmt.Println(string(html))

			S3Provider := s3.NewS3Provider(viper.GetString("S3Endpoint"), viper.GetString("S3AccessKey"), viper.GetString("S3SecretKey"))

			//create user's bucket
			err2 := S3Provider.CreateBucket(u.User)
			if err2 != nil {
				panic(err)
			}

			//upload html to s3 bucket
			//err := S3Provider.Upload("./tmp/"+u.User+"./index.html")

			c.JSON(http.StatusOK, gin.H{"url": "https://s3.arthurma.com.tw/" + u.User + "/index.html"})
		}
	})

	/*router.POST("/webhook", func(c *gin.Context) {

	})*/

	router.Run(":8080")
}
<commit_msg>Upload HTML to file server after parsed<commit_after>package main

import (
	"io"

	"github.com/4406arthur/blog-daddy/adapters/git"
	"github.com/4406arthur/blog-daddy/providers/s3"
	"gopkg.in/russross/blackfriday.v2"

	"log"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

type (
	Article struct {
		Name    string
		ModTime string
	}
)

type MarkdownParser struct {
	reader io.Reader
}

func NewMarkdownParser(r io.Reader) *MarkdownParser {
	return &MarkdownParser{r}
}

func (m *MarkdownParser) Read(p []byte) (n int, err error) {
	n, err = m.reader.Read(p)
	copy(p, blackfriday.Run(p))

	return n, err
}

func main() {
	viper.SetConfigName("config")
	viper.AddConfigPath(".")
	//viper.AddConfigPath("/var/run/secret")
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		panic(err)
	}

	router := gin.Default()
	router.LoadHTMLGlob("templates/*")

	router.POST("/setup", func(c *gin.Context) {
		var u git.User
		if c.BindJSON(&u) == nil {
			//minio cannot create bucket name with uppercase
			//and github is not case sensitive
			//trans to lowercase is not dangerous
			u.User = strings.ToLower(u.User)
			GithubHandler := git.NewGitHandler("https://api.github.com")
			mds := make([]git.GitRepoContent, 0)
			mds, err := GithubHandler.FetchRepoContents(u)
			if err != nil {
				panic(err)
			}
			log.Printf("You have %s\n", mds)

			resp, _ := http.Get(mds[0].Download_Url)
			defer resp.Body.Close()

			m := NewMarkdownParser(resp.Body)

			// html, _ := ioutil.ReadAll(m)

			// fmt.Println(string(html))

			S3Provider := s3.NewS3Provider(viper.GetString("S3Endpoint"), viper.GetString("S3AccessKey"), viper.GetString("S3SecretKey"))

			//create user's bucket
			err2 := S3Provider.CreateBucket(u.User)
			if err2 != nil {
				panic(err)
			}

			//upload html to s3 bucket
			err = S3Provider.Upload(u.User, "index.html", m)

			c.JSON(http.StatusOK, gin.H{"url": "https://s3.arthurma.com.tw/" + u.User + "/index.html"})
		}
	})

	/*router.POST("/webhook", func(c *gin.Context) {

	})*/

	router.Run(":8080")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"os"
)

func main() {
	os.Exit(run(NewApp()))
}

const exitOk = iota

func run(app *App) int {
	return exitOk
}

// App represent application and
// holds configuration.
type App struct {
	osargs []string
	stdout io.Writer
	logger *log.Logger
}

// NewApp create application.
func NewApp() *App {
	return &App{
		osargs: os.Args,
		stdout: os.Stdout,
		logger: log.New(os.Stderr, "", 0)}
}

// Log prints to logger like log.Print.
func (a *App) Log(v ...interface{}) {
	a.logger.Print(v...)
}

// Logf prints to logger like log.Printf.
func (a *App) Logf(format string, v ...interface{}) {
	a.logger.Printf(format, v...)
}

// Logln prints to logger like log.Println.
func (a *App) Logln(v ...interface{}) {
	a.logger.Println(v...)
}

func (a *App) write(w io.Writer, v ...interface{}) {
	_, err := fmt.Fprint(w, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) writef(w io.Writer, format string, v ...interface{}) {
	_, err := fmt.Fprintf(w, format, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) writeln(w io.Writer, v ...interface{}) {
	_, err := fmt.Fprintln(w, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

// Printf prints to stdout like fmt.Printf.
func (a *App) Printf(format string, v ...interface{}) {
	a.writef(a.stdout, format, v...)
}
<commit_msg>app: add App.output<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"os"
)

func main() {
	os.Exit(run(NewApp()))
}

const exitOk = iota

func run(app *App) int {
	return exitOk
}

// App represent application and
// holds configuration.
type App struct {
	osargs []string
	stdout io.Writer
	logger *log.Logger
}

// NewApp create application.
func NewApp() *App {
	return &App{
		osargs: os.Args,
		stdout: os.Stdout,
		logger: log.New(os.Stderr, "", 0)}
}

// Log prints to logger like log.Print.
func (a *App) Log(v ...interface{}) {
	a.logger.Print(v...)
}

// Logf prints to logger like log.Printf.
func (a *App) Logf(format string, v ...interface{}) {
	a.logger.Printf(format, v...)
}

// Logln prints to logger like log.Println.
func (a *App) Logln(v ...interface{}) {
	a.logger.Println(v...)
}

const (
	fnPrint = iota
	fnPrintf
	fnPrintln
)

func (a *App) output(typ int, format string, v ...interface{}) {
	var err error
	switch typ {
	case fnPrint:
		_, err = fmt.Fprint(a.stdout, v...)

	case fnPrintf:
		_, err = fmt.Fprintf(a.stdout, format, v...)
	case fnPrintln:
		_, err = fmt.Fprintln(a.stdout, v...)
	}
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) write(w io.Writer, v ...interface{}) {
	_, err := fmt.Fprint(w, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) writef(w io.Writer, format string, v ...interface{}) {
	_, err := fmt.Fprintf(w, format, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) writeln(w io.Writer, v ...interface{}) {
	_, err := fmt.Fprintln(w, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

// Printf prints to stdout like fmt.Printf.
func (a *App) Printf(format string, v ...interface{}) {
	a.output(fnPrintf, format, v...)
}
<|endoftext|>


<commit_before>package consul

import (
	"fmt"
	"io"
	"net"
	"os"
	"time"

	"github.com/hashicorp/consul/tlsutil"
	"github.com/hashicorp/memberlist"
	"github.com/hashicorp/raft"
	"github.com/hashicorp/serf/serf"
)

const (
	DefaultDC          = "dc1"
	DefaultLANSerfPort = 8301
	DefaultWANSerfPort = 8302

	// See docs/guides/performance.html for information on how this value
	// was obtained.
	DefaultRaftMultiplier uint = 5
)

var (
	DefaultRPCAddr = &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 8300}
)

// ProtocolVersionMap is the mapping of Consul protocol versions
// to Serf protocol versions. We mask the Serf protocols using
// our own protocol version.
var protocolVersionMap map[uint8]uint8

func init() {
	protocolVersionMap = map[uint8]uint8{
		1: 4,
		2: 4,
		3: 4,
	}
}

// Config is used to configure the server
type Config struct {
	// Bootstrap mode is used to bring up the first Consul server.
	// It is required so that it can elect a leader without any
	// other nodes being present
	Bootstrap bool

	// BootstrapExpect mode is used to automatically bring up a collection of
	// Consul servers. This can be used to automatically bring up a collection
	// of nodes.
	BootstrapExpect int

	// Datacenter is the datacenter this Consul server represents
	Datacenter string

	// DataDir is the directory to store our state in
	DataDir string

	// DevMode is used to enable a development server mode.
	DevMode bool

	// Node name is the name we use to advertise. Defaults to hostname.
	NodeName string

	// Domain is the DNS domain for the records. Defaults to "consul."
	Domain string

	// RaftConfig is the configuration used for Raft in the local DC
	RaftConfig *raft.Config

	// RPCAddr is the RPC address used by Consul. This should be reachable
	// by the WAN and LAN
	RPCAddr *net.TCPAddr

	// RPCAdvertise is the address that is advertised to other nodes for
	// the RPC endpoint. This can differ from the RPC address, if for example
	// the RPCAddr is unspecified "0.0.0.0:8300", but this address must be
	// reachable
	RPCAdvertise *net.TCPAddr

	// SerfLANConfig is the configuration for the intra-dc serf
	SerfLANConfig *serf.Config

	// SerfWANConfig is the configuration for the cross-dc serf
	SerfWANConfig *serf.Config

	// ReconcileInterval controls how often we reconcile the strongly
	// consistent store with the Serf info. This is used to handle nodes
	// that are force removed, as well as intermittent unavailability during
	// leader election.
	ReconcileInterval time.Duration

	// LogOutput is the location to write logs to. If this is not set,
	// logs will go to stderr.
	LogOutput io.Writer

	// ProtocolVersion is the protocol version to speak. This must be between
	// ProtocolVersionMin and ProtocolVersionMax.
	ProtocolVersion uint8

	// VerifyIncoming is used to verify the authenticity of incoming connections.
	// This means that TCP requests are forbidden, only allowing for TLS. TLS connections
	// must match a provided certificate authority. This can be used to force client auth.
	VerifyIncoming bool

	// VerifyOutgoing is used to verify the authenticity of outgoing connections.
	// This means that TLS requests are used, and TCP requests are not made. TLS connections
	// must match a provided certificate authority. This is used to verify authenticity of
	// server nodes.
	VerifyOutgoing bool

	// VerifyServerHostname is used to enable hostname verification of servers. This
	// ensures that the certificate presented is valid for server.<datacenter>.<domain>.
	// This prevents a compromised client from being restarted as a server, and then
	// intercepting request traffic as well as being added as a raft peer. This should be
	// enabled by default with VerifyOutgoing, but for legacy reasons we cannot break
	// existing clients.
	VerifyServerHostname bool

	// CAFile is a path to a certificate authority file. This is used with VerifyIncoming
	// or VerifyOutgoing to verify the TLS connection.
	CAFile string

	// CertFile is used to provide a TLS certificate that is used for serving TLS connections.
	// Must be provided to serve TLS connections.
	CertFile string

	// KeyFile is used to provide a TLS key that is used for serving TLS connections.
	// Must be provided to serve TLS connections.
	KeyFile string

	// ServerName is used with the TLS certificate to ensure the name we
	// provide matches the certificate
	ServerName string

	// RejoinAfterLeave controls our interaction with Serf.
	// When set to false (default), a leave causes a Consul to not rejoin
	// the cluster until an explicit join is received. If this is set to
	// true, we ignore the leave, and rejoin the cluster on start.
	RejoinAfterLeave bool

	// Build is a string that is gossiped around, and can be used to help
	// operators track which versions are actively deployed
	Build string

	// ACLToken is the default token to use when making a request.
	// If not provided, the anonymous token is used. This enables
	// backwards compatibility as well.
	ACLToken string

	// ACLMasterToken is used to bootstrap the ACL system. It should be specified
	// on the servers in the ACLDatacenter. When the leader comes online, it ensures
	// that the Master token is available. This provides the initial token.
	ACLMasterToken string

	// ACLDatacenter provides the authoritative datacenter for ACL
	// tokens. If not provided, ACL verification is disabled.
	ACLDatacenter string

	// ACLTTL controls the time-to-live of cached ACL policies.
	// It can be set to zero to disable caching, but this adds
	// a substantial cost.
	ACLTTL time.Duration

	// ACLDefaultPolicy is used to control the ACL interaction when
	// there is no defined policy. This can be "allow" which means
	// ACLs are used to black-list, or "deny" which means ACLs are
	// white-lists.
	ACLDefaultPolicy string

	// ACLDownPolicy controls the behavior of ACLs if the ACLDatacenter
	// cannot be contacted. It can be either "deny" to deny all requests,
	// or "extend-cache" which ignores the ACLCacheInterval and uses
	// cached policies. If a policy is not in the cache, it acts like deny.
	// "allow" can be used to allow all requests. This is not recommended.
	ACLDownPolicy string

	// ACLReplicationToken is used to fetch ACLs from the ACLDatacenter in
	// order to replicate them locally. Setting this to a non-empty value
	// also enables replication. Replication is only available in datacenters
	// other than the ACLDatacenter.
	ACLReplicationToken string

	// ACLReplicationInterval is the interval at which replication passes
	// will occur. Queries to the ACLDatacenter may block, so replication
	// can happen less often than this, but the interval forms the upper
	// limit to how fast we will go if there was constant ACL churn on the
	// remote end.
	ACLReplicationInterval time.Duration

	// ACLReplicationApplyLimit is the max number of replication-related
	// apply operations that we allow during a one second period. This is
	// used to limit the amount of Raft bandwidth used for replication.
	ACLReplicationApplyLimit int

	// TombstoneTTL is used to control how long KV tombstones are retained.
	// This provides a window of time where the X-Consul-Index is monotonic.
	// Outside this window, the index may not be monotonic. This is a result
	// of a few trade offs:
	// 1) The index is defined by the data view and not globally. This is a
	// performance optimization that prevents any write from incrementing the
	// index for all data views.
	// 2) Tombstones are not kept indefinitely, since otherwise storage required
	// is also monotonic. This prevents deletes from reducing the disk space
	// used.
	// In theory, neither of these are intrinsic limitations, however for the
	// purposes of building a practical system, they are reasonable trade offs.
	//
	// It is also possible to set this to an incredibly long time, thereby
	// simulating infinite retention. This is not recommended however.
	//
	TombstoneTTL time.Duration

	// TombstoneTTLGranularity is used to control how granular the timers are
	// for the Tombstone GC. This is used to batch the GC of many keys together
	// to reduce overhead. It is unlikely a user would ever need to tune this.
	TombstoneTTLGranularity time.Duration

	// Minimum Session TTL
	SessionTTLMin time.Duration

	// ServerUp callback can be used to trigger a notification that
	// a Consul server is now up and known about.
	ServerUp func()

	// UserEventHandler callback can be used to handle incoming
	// user events. This function should not block.
	UserEventHandler func(serf.UserEvent)

	// DisableCoordinates controls features related to network coordinates.
	DisableCoordinates bool

	// CoordinateUpdatePeriod controls how long a server batches coordinate
	// updates before applying them in a Raft transaction. A larger period
	// leads to fewer Raft transactions, but also the stored coordinates
	// being more stale.
	CoordinateUpdatePeriod time.Duration

	// CoordinateUpdateBatchSize controls the maximum number of updates a
	// server batches before applying them in a Raft transaction.
	CoordinateUpdateBatchSize int

	// CoordinateUpdateMaxBatches controls the maximum number of batches we
	// are willing to apply in one period. After this limit we will issue a
	// warning and discard the remaining updates.
	CoordinateUpdateMaxBatches int

	// RPCHoldTimeout is how long an RPC can be "held" before it is errored.
	// This is used to paper over a loss of leadership by instead holding RPCs,
	// so that the caller experiences a slow response rather than an error.
	// This period is meant to be long enough for a leader election to take
	// place, and a small jitter is applied to avoid a thundering herd.
	RPCHoldTimeout time.Duration
}

// CheckVersion is used to check if the ProtocolVersion is valid
func (c *Config) CheckVersion() error {
	if c.ProtocolVersion < ProtocolVersionMin {
		return fmt.Errorf("Protocol version '%d' too low. Must be in range: [%d, %d]",
			c.ProtocolVersion, ProtocolVersionMin, ProtocolVersionMax)
	} else if c.ProtocolVersion > ProtocolVersionMax {
		return fmt.Errorf("Protocol version '%d' too high. Must be in range: [%d, %d]",
			c.ProtocolVersion, ProtocolVersionMin, ProtocolVersionMax)
	}
	return nil
}

// CheckACL is used to sanity check the ACL configuration
func (c *Config) CheckACL() error {
	switch c.ACLDefaultPolicy {
	case "allow":
	case "deny":
	default:
		return fmt.Errorf("Unsupported default ACL policy: %s", c.ACLDefaultPolicy)
	}
	switch c.ACLDownPolicy {
	case "allow":
	case "deny":
	case "extend-cache":
	default:
		return fmt.Errorf("Unsupported down ACL policy: %s", c.ACLDownPolicy)
	}
	return nil
}

// DefaultConfig is used to return a sane default configuration
func DefaultConfig() *Config {
	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	conf := &Config{
		Datacenter:               DefaultDC,
		NodeName:                 hostname,
		RPCAddr:                  DefaultRPCAddr,
		RaftConfig:               raft.DefaultConfig(),
		SerfLANConfig:            serf.DefaultConfig(),
		SerfWANConfig:            serf.DefaultConfig(),
		ReconcileInterval:        60 * time.Second,
		ProtocolVersion:          ProtocolVersion2Compatible,
		ACLTTL:                   30 * time.Second,
		ACLDefaultPolicy:         "allow",
		ACLDownPolicy:            "extend-cache",
		ACLReplicationInterval:   30 * time.Second,
		ACLReplicationApplyLimit: 100, // ops / sec
		TombstoneTTL:             15 * time.Minute,
		TombstoneTTLGranularity:  30 * time.Second,
		SessionTTLMin:            10 * time.Second,
		DisableCoordinates:       false,

		// These are tuned to provide a total throughput of 128 updates
		// per second. If you update these, you should update the client-
		// side SyncCoordinateRateTarget parameter accordingly.
		CoordinateUpdatePeriod:     5 * time.Second,
		CoordinateUpdateBatchSize:  128,
		CoordinateUpdateMaxBatches: 5,

		// Hold an RPC for up to 5 seconds by default
		RPCHoldTimeout: 5 * time.Second,
	}

	// Increase our reap interval to 3 days instead of 24h.
	conf.SerfLANConfig.ReconnectTimeout = 3 * 24 * time.Hour
	conf.SerfWANConfig.ReconnectTimeout = 3 * 24 * time.Hour

	// WAN Serf should use the WAN timing, since we are using it
	// to communicate between DC's
	conf.SerfWANConfig.MemberlistConfig = memberlist.DefaultWANConfig()

	// Ensure we don't have port conflicts
	conf.SerfLANConfig.MemberlistConfig.BindPort = DefaultLANSerfPort
	conf.SerfWANConfig.MemberlistConfig.BindPort = DefaultWANSerfPort

	// Enable interoperability with unversioned Raft library, and don't
	// start using new ID-based features yet.
	conf.RaftConfig.ProtocolVersion = 1
	conf.ScaleRaft(DefaultRaftMultiplier)

	// Disable shutdown on removal
	conf.RaftConfig.ShutdownOnRemove = false

	return conf
}

// ScaleRaft sets the config to have Raft timing parameters scaled by the given
// performance multiplier. This is done in an idempotent way so it's not tricky
// to call this when composing configurations and potentially calling this
// multiple times on the same structure.
func (c *Config) ScaleRaft(raftMultRaw uint) {
	raftMult := time.Duration(raftMultRaw)

	def := raft.DefaultConfig()
	c.RaftConfig.HeartbeatTimeout = raftMult * def.HeartbeatTimeout
	c.RaftConfig.ElectionTimeout = raftMult * def.ElectionTimeout
	c.RaftConfig.CommitTimeout = raftMult * def.CommitTimeout
	c.RaftConfig.LeaderLeaseTimeout = raftMult * def.LeaderLeaseTimeout
}

func (c *Config) tlsConfig() *tlsutil.Config {
	tlsConf := &tlsutil.Config{
		VerifyIncoming:       c.VerifyIncoming,
		VerifyOutgoing:       c.VerifyOutgoing,
		VerifyServerHostname: c.VerifyServerHostname,
		CAFile:               c.CAFile,
		CertFile:             c.CertFile,
		KeyFile:              c.KeyFile,
		NodeName:             c.NodeName,
		ServerName:           c.ServerName,
		Domain:               c.Domain,
	}
	return tlsConf
}
<commit_msg>Increases RPC hold timeout for new default timing.<commit_after>package consul

import (
	"fmt"
	"io"
	"net"
	"os"
	"time"

	"github.com/hashicorp/consul/tlsutil"
	"github.com/hashicorp/memberlist"
	"github.com/hashicorp/raft"
	"github.com/hashicorp/serf/serf"
)

const (
	DefaultDC          = "dc1"
	DefaultLANSerfPort = 8301
	DefaultWANSerfPort = 8302

	// See docs/guides/performance.html for information on how this value
	// was obtained.
	DefaultRaftMultiplier uint = 5
)

var (
	DefaultRPCAddr = &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 8300}
)

// ProtocolVersionMap is the mapping of Consul protocol versions
// to Serf protocol versions. We mask the Serf protocols using
// our own protocol version.
var protocolVersionMap map[uint8]uint8

func init() {
	protocolVersionMap = map[uint8]uint8{
		1: 4,
		2: 4,
		3: 4,
	}
}

// Config is used to configure the server
type Config struct {
	// Bootstrap mode is used to bring up the first Consul server.
	// It is required so that it can elect a leader without any
	// other nodes being present
	Bootstrap bool

	// BootstrapExpect mode is used to automatically bring up a collection of
	// Consul servers. This can be used to automatically bring up a collection
	// of nodes.
	BootstrapExpect int

	// Datacenter is the datacenter this Consul server represents
	Datacenter string

	// DataDir is the directory to store our state in
	DataDir string

	// DevMode is used to enable a development server mode.
	DevMode bool

	// Node name is the name we use to advertise. Defaults to hostname.
	NodeName string

	// Domain is the DNS domain for the records. Defaults to "consul."
	Domain string

	// RaftConfig is the configuration used for Raft in the local DC
	RaftConfig *raft.Config

	// RPCAddr is the RPC address used by Consul. This should be reachable
	// by the WAN and LAN
	RPCAddr *net.TCPAddr

	// RPCAdvertise is the address that is advertised to other nodes for
	// the RPC endpoint. This can differ from the RPC address, if for example
	// the RPCAddr is unspecified "0.0.0.0:8300", but this address must be
	// reachable
	RPCAdvertise *net.TCPAddr

	// SerfLANConfig is the configuration for the intra-dc serf
	SerfLANConfig *serf.Config

	// SerfWANConfig is the configuration for the cross-dc serf
	SerfWANConfig *serf.Config

	// ReconcileInterval controls how often we reconcile the strongly
	// consistent store with the Serf info. This is used to handle nodes
	// that are force removed, as well as intermittent unavailability during
	// leader election.
	ReconcileInterval time.Duration

	// LogOutput is the location to write logs to. If this is not set,
	// logs will go to stderr.
	LogOutput io.Writer

	// ProtocolVersion is the protocol version to speak. This must be between
	// ProtocolVersionMin and ProtocolVersionMax.
	ProtocolVersion uint8

	// VerifyIncoming is used to verify the authenticity of incoming connections.
	// This means that TCP requests are forbidden, only allowing for TLS. TLS connections
	// must match a provided certificate authority. This can be used to force client auth.
	VerifyIncoming bool

	// VerifyOutgoing is used to verify the authenticity of outgoing connections.
	// This means that TLS requests are used, and TCP requests are not made. TLS connections
	// must match a provided certificate authority. This is used to verify authenticity of
	// server nodes.
	VerifyOutgoing bool

	// VerifyServerHostname is used to enable hostname verification of servers. This
	// ensures that the certificate presented is valid for server.<datacenter>.<domain>.
	// This prevents a compromised client from being restarted as a server, and then
	// intercepting request traffic as well as being added as a raft peer. This should be
	// enabled by default with VerifyOutgoing, but for legacy reasons we cannot break
	// existing clients.
	VerifyServerHostname bool

	// CAFile is a path to a certificate authority file. This is used with VerifyIncoming
	// or VerifyOutgoing to verify the TLS connection.
	CAFile string

	// CertFile is used to provide a TLS certificate that is used for serving TLS connections.
	// Must be provided to serve TLS connections.
	CertFile string

	// KeyFile is used to provide a TLS key that is used for serving TLS connections.
	// Must be provided to serve TLS connections.
	KeyFile string

	// ServerName is used with the TLS certificate to ensure the name we
	// provide matches the certificate
	ServerName string

	// RejoinAfterLeave controls our interaction with Serf.
	// When set to false (default), a leave causes a Consul to not rejoin
	// the cluster until an explicit join is received. If this is set to
	// true, we ignore the leave, and rejoin the cluster on start.
	RejoinAfterLeave bool

	// Build is a string that is gossiped around, and can be used to help
	// operators track which versions are actively deployed
	Build string

	// ACLToken is the default token to use when making a request.
	// If not provided, the anonymous token is used. This enables
	// backwards compatibility as well.
	ACLToken string

	// ACLMasterToken is used to bootstrap the ACL system. It should be specified
	// on the servers in the ACLDatacenter. When the leader comes online, it ensures
	// that the Master token is available. This provides the initial token.
	ACLMasterToken string

	// ACLDatacenter provides the authoritative datacenter for ACL
	// tokens. If not provided, ACL verification is disabled.
	ACLDatacenter string

	// ACLTTL controls the time-to-live of cached ACL policies.
	// It can be set to zero to disable caching, but this adds
	// a substantial cost.
	ACLTTL time.Duration

	// ACLDefaultPolicy is used to control the ACL interaction when
	// there is no defined policy. This can be "allow" which means
	// ACLs are used to black-list, or "deny" which means ACLs are
	// white-lists.
	ACLDefaultPolicy string

	// ACLDownPolicy controls the behavior of ACLs if the ACLDatacenter
	// cannot be contacted. It can be either "deny" to deny all requests,
	// or "extend-cache" which ignores the ACLCacheInterval and uses
	// cached policies. If a policy is not in the cache, it acts like deny.
	// "allow" can be used to allow all requests. This is not recommended.
	ACLDownPolicy string

	// ACLReplicationToken is used to fetch ACLs from the ACLDatacenter in
	// order to replicate them locally. Setting this to a non-empty value
	// also enables replication. Replication is only available in datacenters
	// other than the ACLDatacenter.
	ACLReplicationToken string

	// ACLReplicationInterval is the interval at which replication passes
	// will occur. Queries to the ACLDatacenter may block, so replication
	// can happen less often than this, but the interval forms the upper
	// limit to how fast we will go if there was constant ACL churn on the
	// remote end.
	ACLReplicationInterval time.Duration

	// ACLReplicationApplyLimit is the max number of replication-related
	// apply operations that we allow during a one second period. This is
	// used to limit the amount of Raft bandwidth used for replication.
	ACLReplicationApplyLimit int

	// TombstoneTTL is used to control how long KV tombstones are retained.
	// This provides a window of time where the X-Consul-Index is monotonic.
	// Outside this window, the index may not be monotonic. This is a result
	// of a few trade offs:
	// 1) The index is defined by the data view and not globally. This is a
	// performance optimization that prevents any write from incrementing the
	// index for all data views.
	// 2) Tombstones are not kept indefinitely, since otherwise storage required
	// is also monotonic. This prevents deletes from reducing the disk space
	// used.
	// In theory, neither of these are intrinsic limitations, however for the
	// purposes of building a practical system, they are reasonable trade offs.
	//
	// It is also possible to set this to an incredibly long time, thereby
	// simulating infinite retention. This is not recommended however.
	//
	TombstoneTTL time.Duration

	// TombstoneTTLGranularity is used to control how granular the timers are
	// for the Tombstone GC. This is used to batch the GC of many keys together
	// to reduce overhead. It is unlikely a user would ever need to tune this.
	TombstoneTTLGranularity time.Duration

	// Minimum Session TTL
	SessionTTLMin time.Duration

	// ServerUp callback can be used to trigger a notification that
	// a Consul server is now up and known about.
	ServerUp func()

	// UserEventHandler callback can be used to handle incoming
	// user events. This function should not block.
	UserEventHandler func(serf.UserEvent)

	// DisableCoordinates controls features related to network coordinates.
	DisableCoordinates bool

	// CoordinateUpdatePeriod controls how long a server batches coordinate
	// updates before applying them in a Raft transaction. A larger period
	// leads to fewer Raft transactions, but also the stored coordinates
	// being more stale.
	CoordinateUpdatePeriod time.Duration

	// CoordinateUpdateBatchSize controls the maximum number of updates a
	// server batches before applying them in a Raft transaction.
	CoordinateUpdateBatchSize int

	// CoordinateUpdateMaxBatches controls the maximum number of batches we
	// are willing to apply in one period. After this limit we will issue a
	// warning and discard the remaining updates.
	CoordinateUpdateMaxBatches int

	// RPCHoldTimeout is how long an RPC can be "held" before it is errored.
	// This is used to paper over a loss of leadership by instead holding RPCs,
	// so that the caller experiences a slow response rather than an error.
	// This period is meant to be long enough for a leader election to take
	// place, and a small jitter is applied to avoid a thundering herd.
	RPCHoldTimeout time.Duration
}

// CheckVersion is used to check if the ProtocolVersion is valid
func (c *Config) CheckVersion() error {
	if c.ProtocolVersion < ProtocolVersionMin {
		return fmt.Errorf("Protocol version '%d' too low. Must be in range: [%d, %d]",
			c.ProtocolVersion, ProtocolVersionMin, ProtocolVersionMax)
	} else if c.ProtocolVersion > ProtocolVersionMax {
		return fmt.Errorf("Protocol version '%d' too high. Must be in range: [%d, %d]",
			c.ProtocolVersion, ProtocolVersionMin, ProtocolVersionMax)
	}
	return nil
}

// CheckACL is used to sanity check the ACL configuration
func (c *Config) CheckACL() error {
	switch c.ACLDefaultPolicy {
	case "allow":
	case "deny":
	default:
		return fmt.Errorf("Unsupported default ACL policy: %s", c.ACLDefaultPolicy)
	}
	switch c.ACLDownPolicy {
	case "allow":
	case "deny":
	case "extend-cache":
	default:
		return fmt.Errorf("Unsupported down ACL policy: %s", c.ACLDownPolicy)
	}
	return nil
}

// DefaultConfig is used to return a sane default configuration
func DefaultConfig() *Config {
	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	conf := &Config{
		Datacenter:               DefaultDC,
		NodeName:                 hostname,
		RPCAddr:                  DefaultRPCAddr,
		RaftConfig:               raft.DefaultConfig(),
		SerfLANConfig:            serf.DefaultConfig(),
		SerfWANConfig:            serf.DefaultConfig(),
		ReconcileInterval:        60 * time.Second,
		ProtocolVersion:          ProtocolVersion2Compatible,
		ACLTTL:                   30 * time.Second,
		ACLDefaultPolicy:         "allow",
		ACLDownPolicy:            "extend-cache",
		ACLReplicationInterval:   30 * time.Second,
		ACLReplicationApplyLimit: 100, // ops / sec
		TombstoneTTL:             15 * time.Minute,
		TombstoneTTLGranularity:  30 * time.Second,
		SessionTTLMin:            10 * time.Second,
		DisableCoordinates:       false,

		// These are tuned to provide a total throughput of 128 updates
		// per second. If you update these, you should update the client-
		// side SyncCoordinateRateTarget parameter accordingly.
		CoordinateUpdatePeriod:     5 * time.Second,
		CoordinateUpdateBatchSize:  128,
		CoordinateUpdateMaxBatches: 5,

		// This holds RPCs during leader elections. For the default Raft
		// config the election timeout is 5 seconds, so we set this a
		// bit longer to try to cover that period. This should be more
		// than enough when running in the high performance mode.
		RPCHoldTimeout: 7 * time.Second,
	}

	// Increase our reap interval to 3 days instead of 24h.
	conf.SerfLANConfig.ReconnectTimeout = 3 * 24 * time.Hour
	conf.SerfWANConfig.ReconnectTimeout = 3 * 24 * time.Hour

	// WAN Serf should use the WAN timing, since we are using it
	// to communicate between DC's
	conf.SerfWANConfig.MemberlistConfig = memberlist.DefaultWANConfig()

	// Ensure we don't have port conflicts
	conf.SerfLANConfig.MemberlistConfig.BindPort = DefaultLANSerfPort
	conf.SerfWANConfig.MemberlistConfig.BindPort = DefaultWANSerfPort

	// Enable interoperability with unversioned Raft library, and don't
	// start using new ID-based features yet.
	conf.RaftConfig.ProtocolVersion = 1
	conf.ScaleRaft(DefaultRaftMultiplier)

	// Disable shutdown on removal
	conf.RaftConfig.ShutdownOnRemove = false

	return conf
}

// ScaleRaft sets the config to have Raft timing parameters scaled by the given
// performance multiplier. This is done in an idempotent way so it's not tricky
// to call this when composing configurations and potentially calling this
// multiple times on the same structure.
func (c *Config) ScaleRaft(raftMultRaw uint) {
	raftMult := time.Duration(raftMultRaw)

	def := raft.DefaultConfig()
	c.RaftConfig.HeartbeatTimeout = raftMult * def.HeartbeatTimeout
	c.RaftConfig.ElectionTimeout = raftMult * def.ElectionTimeout
	c.RaftConfig.CommitTimeout = raftMult * def.CommitTimeout
	c.RaftConfig.LeaderLeaseTimeout = raftMult * def.LeaderLeaseTimeout
}

func (c *Config) tlsConfig() *tlsutil.Config {
	tlsConf := &tlsutil.Config{
		VerifyIncoming:       c.VerifyIncoming,
		VerifyOutgoing:       c.VerifyOutgoing,
		VerifyServerHostname: c.VerifyServerHostname,
		CAFile:               c.CAFile,
		CertFile:             c.CertFile,
		KeyFile:              c.KeyFile,
		NodeName:             c.NodeName,
		ServerName:           c.ServerName,
		Domain:               c.Domain,
	}
	return tlsConf
}
<|endoftext|>


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

package contacts

import (
	"context"
	"fmt"
	"time"

	"github.com/keybase/client/go/chat/storage"
	"github.com/keybase/client/go/encrypteddb"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

// ContactCacheStore is used by CachedContactsProvider to store contact cache
// encrypted with device key.
type ContactCacheStore struct {
	encryptedDB *encrypteddb.EncryptedDB
}

func (s *ContactCacheStore) dbKey(uid keybase1.UID) libkb.DbKey {
	return libkb.DbKey{
		Typ: libkb.DBContactResolution,
		Key: fmt.Sprintf("%v", uid),
	}
}

// NewContactCacheStore creates new ContactCacheStore for given global context.
func NewContactCacheStore(g *libkb.GlobalContext) *ContactCacheStore {
	keyFn := func(ctx context.Context) ([32]byte, error) {
		return storage.GetSecretBoxKey(ctx, g, storage.DefaultSecretUI)
	}
	dbFn := func(g *libkb.GlobalContext) *libkb.JSONLocalDb {
		return g.LocalDb
	}
	return &ContactCacheStore{
		encryptedDB: encrypteddb.New(g, dbFn, keyFn),
	}
}

type CachedContactsProvider struct {
	Provider ContactsProvider
	Store    *ContactCacheStore
}

var _ ContactsProvider = (*CachedContactsProvider)(nil)

type cachedLookupResult struct {
	ContactLookupResult
	Resolved bool
	CachedAt time.Time
}

type contactsCache struct {
	Lookups map[string]cachedLookupResult
	Version struct {
		Major int
		Minor int
	}
}

func makeNewContactsCache() (ret contactsCache) {
	ret = contactsCache{
		Lookups: make(map[string]cachedLookupResult),
	}
	ret.Version.Major = cacheCurrentMajorVersion
	ret.Version.Minor = cacheCurrentMinorVersion
	return ret
}

const cacheCurrentMajorVersion = 1
const cacheCurrentMinorVersion = 0

func cachedResultFromLookupResult(v ContactLookupResult, now time.Time) cachedLookupResult {
	return cachedLookupResult{
		ContactLookupResult: v,
		Resolved:            true,
		CachedAt:            now,
	}
}

const contactCacheFreshness = 30 * 24 * time.Hour // approx a month

func (c *contactsCache) findFreshOrSetEmpty(mctx libkb.MetaContext, key string) (res cachedLookupResult, found bool) {
	clock := mctx.G().Clock()
	res, found = c.Lookups[key]
	if !found || clock.Since(res.CachedAt) > contactCacheFreshness {
		// Pre-insert to the cache. If Provider.LookupAll does not find
		// these, they will stay in the cache as unresolved, otherwise they
		// are overwritten.
		res = cachedLookupResult{Resolved: false, CachedAt: clock.Now()}
		c.Lookups[key] = res
		return res, false
	}
	return res, found
}

func (c *CachedContactsProvider) LookupAll(mctx libkb.MetaContext, emails []keybase1.EmailAddress,
	numbers []keybase1.RawPhoneNumber, userRegion keybase1.RegionCode) (res ContactLookupMap, err error) {

	defer mctx.TraceTimed(fmt.Sprintf("CachedContactsProvider#LookupAll(len=%d)", len(emails)+len(numbers)),
		func() error { return nil })()

	res = make(ContactLookupMap)
	if len(emails)+len(numbers) == 0 {
		return res, nil
	}

	now := mctx.G().Clock().Now()

	var conCache contactsCache
	cacheKey := c.Store.dbKey(mctx.CurrentUID())
	found, cerr := c.Store.encryptedDB.Get(mctx.Ctx(), cacheKey, &conCache)
	if cerr != nil {
		mctx.Warning("Unable to pull cache: %s", cerr)
	} else if !found {
		conCache = makeNewContactsCache()
		mctx.Debug("There was no cache, making a new cache object")
	} else {
		mctx.Debug("Fetched cache, current cache size: %d", len(conCache.Lookups))
	}

	var remainingEmails []keybase1.EmailAddress
	var remainingNumbers []keybase1.RawPhoneNumber

	for _, v := range emails {
		key := makeEmailLookupKey(v)
		if cache, found := conCache.findFreshOrSetEmpty(mctx, key); found {
			if cache.Resolved {
				res[key] = cache.ContactLookupResult
			}
		} else {
			remainingEmails = append(remainingEmails, v)
		}
	}

	for _, v := range numbers {
		key := makePhoneLookupKey(v)
		if cache, found := conCache.findFreshOrSetEmpty(mctx, key); found {
			if cache.Resolved {
				res[key] = cache.ContactLookupResult
			}
		} else {
			remainingNumbers = append(remainingNumbers, v)
		}
	}

	mctx.Debug("After checking cache, %d emails and %d numbers left to be looked up", len(remainingEmails), len(remainingNumbers))

	if len(remainingEmails)+len(remainingNumbers) > 0 {
		apiRes, err := c.Provider.LookupAll(mctx, remainingEmails, remainingNumbers, userRegion)
		if err == nil {
			for k, v := range apiRes {
				res[k] = v
				conCache.Lookups[k] = cachedResultFromLookupResult(v, now)
			}
		} else {
			mctx.Warning("Unable to call Provider.LookupAll, returning only cached results: %s", err)
		}

		cerr := c.Store.encryptedDB.Put(mctx.Ctx(), cacheKey, conCache)
		if cerr != nil {
			mctx.Warning("Unable to update cache: %s", cerr)
		}
	}

	return res, nil
}

func (c *CachedContactsProvider) FillUsernames(mctx libkb.MetaContext, res []keybase1.ProcessedContact) {
	c.Provider.FillUsernames(mctx, res)
}

func (c *CachedContactsProvider) FillFollowing(mctx libkb.MetaContext, res []keybase1.ProcessedContact) {
	c.Provider.FillFollowing(mctx, res)
}
<commit_msg>Add lock in contact cache (#17618)<commit_after>// Copyright 2019 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package contacts

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/keybase/client/go/chat/storage"
	"github.com/keybase/client/go/encrypteddb"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

// ContactCacheStore is used by CachedContactsProvider to store contact cache
// encrypted with device key.
type ContactCacheStore struct {
	encryptedDB *encrypteddb.EncryptedDB
}

func (s *ContactCacheStore) dbKey(uid keybase1.UID) libkb.DbKey {
	return libkb.DbKey{
		Typ: libkb.DBContactResolution,
		Key: fmt.Sprintf("%v", uid),
	}
}

// NewContactCacheStore creates new ContactCacheStore for given global context.
func NewContactCacheStore(g *libkb.GlobalContext) *ContactCacheStore {
	keyFn := func(ctx context.Context) ([32]byte, error) {
		return storage.GetSecretBoxKey(ctx, g, storage.DefaultSecretUI)
	}
	dbFn := func(g *libkb.GlobalContext) *libkb.JSONLocalDb {
		return g.LocalDb
	}
	return &ContactCacheStore{
		encryptedDB: encrypteddb.New(g, dbFn, keyFn),
	}
}

type CachedContactsProvider struct {
	lock sync.Mutex

	Provider ContactsProvider
	Store    *ContactCacheStore
}

var _ ContactsProvider = (*CachedContactsProvider)(nil)

type cachedLookupResult struct {
	ContactLookupResult
	Resolved bool
	CachedAt time.Time
}

type contactsCache struct {
	Lookups map[string]cachedLookupResult
	Version struct {
		Major int
		Minor int
	}
}

func makeNewContactsCache() (ret contactsCache) {
	ret = contactsCache{
		Lookups: make(map[string]cachedLookupResult),
	}
	ret.Version.Major = cacheCurrentMajorVersion
	ret.Version.Minor = cacheCurrentMinorVersion
	return ret
}

const cacheCurrentMajorVersion = 1
const cacheCurrentMinorVersion = 0

func cachedResultFromLookupResult(v ContactLookupResult, now time.Time) cachedLookupResult {
	return cachedLookupResult{
		ContactLookupResult: v,
		Resolved:            true,
		CachedAt:            now,
	}
}

const contactCacheFreshness = 30 * 24 * time.Hour // approx a month

func (c *contactsCache) findFreshOrSetEmpty(mctx libkb.MetaContext, key string) (res cachedLookupResult, found bool) {
	clock := mctx.G().Clock()
	res, found = c.Lookups[key]
	if !found || clock.Since(res.CachedAt) > contactCacheFreshness {
		// Pre-insert to the cache. If Provider.LookupAll does not find
		// these, they will stay in the cache as unresolved, otherwise they
		// are overwritten.
		res = cachedLookupResult{Resolved: false, CachedAt: clock.Now()}
		c.Lookups[key] = res
		return res, false
	}
	return res, found
}

func (c *CachedContactsProvider) LookupAll(mctx libkb.MetaContext, emails []keybase1.EmailAddress,
	numbers []keybase1.RawPhoneNumber, userRegion keybase1.RegionCode) (res ContactLookupMap, err error) {

	defer mctx.TraceTimed(fmt.Sprintf("CachedContactsProvider#LookupAll(len=%d)", len(emails)+len(numbers)),
		func() error { return nil })()

	res = make(ContactLookupMap)
	if len(emails)+len(numbers) == 0 {
		return res, nil
	}

	now := mctx.G().Clock().Now()

	// This is a rather long-lived lock, because normally it will be held
	// through the entire duration of the lookup, but:
	// - We don't expect this to be called concurrently, or repeatedly, without
	//   user's interaction.
	// - We want to avoid looking up the same assertion multiple times (burning
	//   through the rate limit), while keeping the locking strategy simple.
	c.lock.Lock()
	defer c.lock.Unlock()

	var conCache contactsCache
	cacheKey := c.Store.dbKey(mctx.CurrentUID())
	found, cerr := c.Store.encryptedDB.Get(mctx.Ctx(), cacheKey, &conCache)
	if cerr != nil {
		mctx.Warning("Unable to pull cache: %s", cerr)
	} else if !found {
		conCache = makeNewContactsCache()
		mctx.Debug("There was no cache, making a new cache object")
	} else {
		mctx.Debug("Fetched cache, current cache size: %d", len(conCache.Lookups))
	}

	var remainingEmails []keybase1.EmailAddress
	var remainingNumbers []keybase1.RawPhoneNumber

	for _, v := range emails {
		key := makeEmailLookupKey(v)
		if cache, found := conCache.findFreshOrSetEmpty(mctx, key); found {
			if cache.Resolved {
				res[key] = cache.ContactLookupResult
			}
		} else {
			remainingEmails = append(remainingEmails, v)
		}
	}

	for _, v := range numbers {
		key := makePhoneLookupKey(v)
		if cache, found := conCache.findFreshOrSetEmpty(mctx, key); found {
			if cache.Resolved {
				res[key] = cache.ContactLookupResult
			}
		} else {
			remainingNumbers = append(remainingNumbers, v)
		}
	}

	mctx.Debug("After checking cache, %d emails and %d numbers left to be looked up", len(remainingEmails), len(remainingNumbers))

	if len(remainingEmails)+len(remainingNumbers) > 0 {
		apiRes, err := c.Provider.LookupAll(mctx, remainingEmails, remainingNumbers, userRegion)
		if err == nil {
			for k, v := range apiRes {
				res[k] = v
				conCache.Lookups[k] = cachedResultFromLookupResult(v, now)
			}
		} else {
			mctx.Warning("Unable to call Provider.LookupAll, returning only cached results: %s", err)
		}

		cerr := c.Store.encryptedDB.Put(mctx.Ctx(), cacheKey, conCache)
		if cerr != nil {
			mctx.Warning("Unable to update cache: %s", cerr)
		}
	}

	return res, nil
}

func (c *CachedContactsProvider) FillUsernames(mctx libkb.MetaContext, res []keybase1.ProcessedContact) {
	c.Provider.FillUsernames(mctx, res)
}

func (c *CachedContactsProvider) FillFollowing(mctx libkb.MetaContext, res []keybase1.ProcessedContact) {
	c.Provider.FillFollowing(mctx, res)
}
<|endoftext|>


<commit_before>package libkb

import (
	"bufio"
	"fmt"
	"github.com/keybase/client/protocol/go"
	"io"
	"os"
	"os/exec"
	"strings"
)

//
// some borrowed from here:
//
//  https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
//
// Under the Apache 2.0 license
//

type Pinentry struct {
	initRes *error
	path    string
	term    string
	tty     string
}

func NewPinentry() *Pinentry {
	return &Pinentry{path: ""}
}

func (pe *Pinentry) Init() (error, error) {
	if pe.initRes != nil {
		return *pe.initRes, nil
	}
	err, fatalerr := pe.FindProgram()
	if err == nil {
		err = pe.GetTerminalName()
	}
	pe.term = os.Getenv("TERM")
	pe.initRes = &err
	return err, fatalerr
}

func (pe *Pinentry) SetInitError(e error) {
	pe.initRes = &e
}

func (pe *Pinentry) FindProgram() (error, error) {
	prog := G.Env.GetPinentry()
	var err, fatalerr error
	if len(prog) > 0 {
		if err = canExec(prog); err == nil {
			pe.path = prog
		} else {
			err = fmt.Errorf("Can't execute given pinentry program '%s': %s",
				prog, err.Error())
			fatalerr = err
		}
	} else if prog, err = FindPinentry(); err == nil {
		pe.path = prog
	}
	return err, fatalerr
}

func (pe *Pinentry) GetTerminalName() error {
	tty, err := os.Readlink("/proc/self/fd/0")
	if err != nil {
		G.Log.Debug("| Can't find terminal name via /proc lookup: %s", err.Error())
	} else {
		G.Log.Debug("| found tty=%s", tty)
		pe.tty = tty
	}
	// Tis not a fatal error.  In particular, it won't work on OSX
	return nil
}

func (pe *Pinentry) Get(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	G.Log.Debug("+ Pinentry::Get()")

	// Do a lazy initialization
	if err, _ = pe.Init(); err != nil {
		return
	}

	inst := pinentryInstance{parent: pe}
	defer inst.Close()

	if err = inst.Init(); err != nil {
		// We probably shouldn't try to use this thing again if we failed
		// to set it up.
		pe.SetInitError(err)
		return
	}
	res, err = inst.Run(arg)
	G.Log.Debug("- Pinentry::Get() -> %s", ErrToOk(err))
	return
}

func (pi *pinentryInstance) Close() {
	pi.stdin.Close()
	pi.cmd.Wait()
}

type pinentryInstance struct {
	parent *Pinentry
	cmd    *exec.Cmd
	stdout io.ReadCloser
	stdin  io.WriteCloser
	br     *bufio.Reader
}

func (pi *pinentryInstance) Set(cmd, val string, errp *error) {
	if val == "" {
		return
	}
	fmt.Fprintf(pi.stdin, "%s %s\n", cmd, val)
	line, _, err := pi.br.ReadLine()
	if err != nil {
		*errp = err
		return
	}
	if string(line) != "OK" {
		*errp = fmt.Errorf("Response to " + cmd + " was " + string(line))
	}
	return
}

func (pi *pinentryInstance) Init() (err error) {

	parent := pi.parent

	G.Log.Debug("+ pinentryInstance::Init()")

	pi.cmd = exec.Command(parent.path)
	pi.stdin, _ = pi.cmd.StdinPipe()
	pi.stdout, _ = pi.cmd.StdoutPipe()

	if err = pi.cmd.Start(); err != nil {
		G.Log.Errorf("unexpected error running pinentry (%s): %s",
			parent.path, err.Error())
		return
	}

	pi.br = bufio.NewReader(pi.stdout)
	lineb, _, err := pi.br.ReadLine()

	if err != nil {
		err = fmt.Errorf("Failed to get getpin greeting: %s", err.Error())
		return
	}

	line := string(lineb)
	if !strings.HasPrefix(line, "OK") {
		err = fmt.Errorf("getpin greeting didn't say 'OK', said: %q", line)
		return
	}

	if len(parent.tty) > 0 {
		pi.Set("OPTION", "ttyname="+parent.tty, &err)
	}
	if len(parent.term) > 0 {
		pi.Set("OPTION", "ttytype="+parent.term, &err)
	}

	G.Log.Debug("- pinentryInstance::Init() -> %s", ErrToOk(err))
	return
}

func descEncode(s string) string {
	s = strings.Replace(s, "%", "%%", -1)
	s = strings.Replace(s, "\n", "%0A", -1)
	return s
}

func (pi *pinentryInstance) Run(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	pi.Set("SETPROMPT", arg.Prompt, &err)
	pi.Set("SETDESC", descEncode(arg.Desc), &err)
	pi.Set("SETOK", arg.Ok, &err)
	pi.Set("SETCANCEL", arg.Cancel, &err)
	pi.Set("SETERROR", arg.Err, &err)

	if err != nil {
		return
	}

	if err = pi.useSecretStore(arg.UseSecretStore); err != nil {
		return
	}

	fmt.Fprintf(pi.stdin, "GETPIN\n")
	var lineb []byte
	lineb, _, err = pi.br.ReadLine()
	if err != nil {
		err = fmt.Errorf("Failed to read line after GETPIN: %v", err)
		return
	}
	line := string(lineb)
	if strings.HasPrefix(line, "D ") {
		res = &keybase1.SecretEntryRes{Text: line[2:]}
	} else if strings.HasPrefix(line, "ERR 83886179 canceled") {
		res = &keybase1.SecretEntryRes{Canceled: true}
	} else if line == "OK" {
		res = &keybase1.SecretEntryRes{}
	} else {
		err = fmt.Errorf("GETPIN response didn't start with D; got %q", line)
	}

	res.StoreSecret = pi.shouldStoreSecret()

	return
}
<commit_msg>Closed #95<commit_after>package libkb

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	keybase1 "github.com/keybase/client/protocol/go"
)

//
// some borrowed from here:
//
//  https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
//
// Under the Apache 2.0 license
//

type Pinentry struct {
	initRes *error
	path    string
	term    string
	tty     string
}

func NewPinentry() *Pinentry {
	return &Pinentry{path: ""}
}

func (pe *Pinentry) Init() (error, error) {
	if pe.initRes != nil {
		return *pe.initRes, nil
	}
	err, fatalerr := pe.FindProgram()
	if err == nil {
		err = pe.GetTerminalName()
	}
	pe.term = os.Getenv("TERM")
	pe.initRes = &err
	return err, fatalerr
}

func (pe *Pinentry) SetInitError(e error) {
	pe.initRes = &e
}

func (pe *Pinentry) FindProgram() (error, error) {
	prog := G.Env.GetPinentry()
	var err, fatalerr error
	if len(prog) > 0 {
		if err = canExec(prog); err == nil {
			pe.path = prog
		} else {
			err = fmt.Errorf("Can't execute given pinentry program '%s': %s",
				prog, err.Error())
			fatalerr = err
		}
	} else if prog, err = FindPinentry(); err == nil {
		pe.path = prog
	}
	return err, fatalerr
}

func (pe *Pinentry) GetTerminalName() error {
	tty, err := os.Readlink("/proc/self/fd/0")
	if err != nil {
		G.Log.Debug("| Can't find terminal name via /proc lookup: %s", err.Error())
	} else {
		G.Log.Debug("| found tty=%s", tty)
		pe.tty = tty
	}
	// Tis not a fatal error.  In particular, it won't work on OSX
	return nil
}

func (pe *Pinentry) Get(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	G.Log.Debug("+ Pinentry::Get()")

	// Do a lazy initialization
	if err, _ = pe.Init(); err != nil {
		return
	}

	inst := pinentryInstance{parent: pe}
	defer inst.Close()

	if err = inst.Init(); err != nil {
		// We probably shouldn't try to use this thing again if we failed
		// to set it up.
		pe.SetInitError(err)
		return
	}
	res, err = inst.Run(arg)
	G.Log.Debug("- Pinentry::Get() -> %s", ErrToOk(err))
	return
}

func (pi *pinentryInstance) Close() {
	pi.stdin.Close()
	pi.cmd.Wait()
}

type pinentryInstance struct {
	parent *Pinentry
	cmd    *exec.Cmd
	stdout io.ReadCloser
	stdin  io.WriteCloser
	br     *bufio.Reader
}

func (pi *pinentryInstance) Set(cmd, val string, errp *error) {
	if val == "" {
		return
	}
	fmt.Fprintf(pi.stdin, "%s %s\n", cmd, val)
	line, _, err := pi.br.ReadLine()
	if err != nil {
		*errp = err
		return
	}
	if string(line) != "OK" {
		*errp = fmt.Errorf("Response to " + cmd + " was " + string(line))
	}
	return
}

func (pi *pinentryInstance) Init() (err error) {

	parent := pi.parent

	G.Log.Debug("+ pinentryInstance::Init()")

	pi.cmd = exec.Command(parent.path)
	pi.stdin, _ = pi.cmd.StdinPipe()
	pi.stdout, _ = pi.cmd.StdoutPipe()

	if err = pi.cmd.Start(); err != nil {
		G.Log.Errorf("unexpected error running pinentry (%s): %s",
			parent.path, err.Error())
		return
	}

	pi.br = bufio.NewReader(pi.stdout)
	lineb, _, err := pi.br.ReadLine()

	if err != nil {
		err = fmt.Errorf("Failed to get getpin greeting: %s", err.Error())
		return
	}

	line := string(lineb)
	if !strings.HasPrefix(line, "OK") {
		err = fmt.Errorf("getpin greeting didn't say 'OK', said: %q", line)
		return
	}

	if len(parent.tty) > 0 {
		pi.Set("OPTION", "ttyname="+parent.tty, &err)
	}
	if len(parent.term) > 0 {
		pi.Set("OPTION", "ttytype="+parent.term, &err)
	}

	G.Log.Debug("- pinentryInstance::Init() -> %s", ErrToOk(err))
	return
}

func descEncode(s string) string {
	s = strings.Replace(s, "%", "%%", -1)
	s = strings.Replace(s, "\n", "%0A", -1)
	return s
}

func resDecode(s string) string {
	s = strings.Replace(s, "%25", "%", -1)
	return s
}

func (pi *pinentryInstance) Run(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	pi.Set("SETPROMPT", arg.Prompt, &err)
	pi.Set("SETDESC", descEncode(arg.Desc), &err)
	pi.Set("SETOK", arg.Ok, &err)
	pi.Set("SETCANCEL", arg.Cancel, &err)
	pi.Set("SETERROR", arg.Err, &err)

	if err != nil {
		return
	}

	if err = pi.useSecretStore(arg.UseSecretStore); err != nil {
		return
	}

	fmt.Fprintf(pi.stdin, "GETPIN\n")
	var lineb []byte
	lineb, _, err = pi.br.ReadLine()
	if err != nil {
		err = fmt.Errorf("Failed to read line after GETPIN: %v", err)
		return
	}
	line := string(lineb)
	if strings.HasPrefix(line, "D ") {
		res = &keybase1.SecretEntryRes{Text: resDecode(line[2:])}
	} else if strings.HasPrefix(line, "ERR 83886179 canceled") {
		res = &keybase1.SecretEntryRes{Canceled: true}
	} else if line == "OK" {
		res = &keybase1.SecretEntryRes{}
	} else {
		return nil, fmt.Errorf("GETPIN response didn't start with D; got %q", line)
	}

	res.StoreSecret = pi.shouldStoreSecret()

	return
}
<|endoftext|>


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

package service

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"golang.org/x/net/context"

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

type ConfigHandler struct {
	libkb.Contextified
	xp     rpc.Transporter
	svc    *Service
	connID libkb.ConnectionID
}

func NewConfigHandler(xp rpc.Transporter, i libkb.ConnectionID, g *libkb.GlobalContext, svc *Service) *ConfigHandler {
	return &ConfigHandler{
		Contextified: libkb.NewContextified(g),
		xp:           xp,
		svc:          svc,
		connID:       i,
	}
}

func (h ConfigHandler) GetCurrentStatus(_ context.Context, sessionID int) (res keybase1.GetCurrentStatusRes, err error) {
	var cs libkb.CurrentStatus
	if cs, err = libkb.GetCurrentStatus(h.G()); err == nil {
		res = cs.Export()
	}
	return
}

func getPlatformInfo() keybase1.PlatformInfo {
	return keybase1.PlatformInfo{
		Os:        runtime.GOOS,
		Arch:      runtime.GOARCH,
		GoVersion: runtime.Version(),
	}
}

func (h ConfigHandler) GetValue(_ context.Context, path string) (ret keybase1.ConfigValue, err error) {
	var i interface{}
	i, err = h.G().Env.GetConfig().GetInterfaceAtPath(path)
	if err != nil {
		return ret, err
	}
	if i == nil {
		ret.IsNull = true
	} else {
		switch v := i.(type) {
		case int:
			ret.I = &v
		case string:
			ret.S = &v
		case bool:
			ret.B = &v
		case float64:
			tmp := int(v)
			ret.I = &tmp
		default:
			var b []byte
			b, err = json.Marshal(v)
			if err == nil {
				tmp := string(b)
				ret.O = &tmp
			}
		}
	}
	return ret, err
}

func (h ConfigHandler) SetValue(_ context.Context, arg keybase1.SetValueArg) (err error) {
	w := h.G().Env.GetConfigWriter()
	if arg.Path == "users" {
		err = fmt.Errorf("The field 'users' cannot be edited for fear of config corruption")
		return err
	}
	switch {
	case arg.Value.IsNull:
		w.SetNullAtPath(arg.Path)
	case arg.Value.S != nil:
		w.SetStringAtPath(arg.Path, *arg.Value.S)
	case arg.Value.I != nil:
		w.SetIntAtPath(arg.Path, *arg.Value.I)
	case arg.Value.B != nil:
		w.SetBoolAtPath(arg.Path, *arg.Value.B)
	case arg.Value.O != nil:
		var jw *jsonw.Wrapper
		jw, err = jsonw.Unmarshal([]byte(*arg.Value.O))
		if err == nil {
			err = w.SetWrapperAtPath(arg.Path, jw)
		}
	default:
		err = fmt.Errorf("Bad type for setting a value")
	}
	if err == nil {
		h.G().ConfigReload()
	}
	return err
}

func (h ConfigHandler) ClearValue(_ context.Context, path string) error {
	h.G().Env.GetConfigWriter().DeleteAtPath(path)
	h.G().ConfigReload()
	return nil
}

func (h ConfigHandler) GetExtendedStatus(_ context.Context, sessionID int) (res keybase1.ExtendedStatus, err error) {
	defer h.G().Trace("ConfigHandler::GetExtendedStatus", func() error { return err })()

	res.Standalone = h.G().Env.GetStandalone()
	res.LogDir = h.G().Env.GetLogDir()

	// Should work in standalone mode too
	if h.G().ConnectionManager != nil {
		res.Clients = h.G().ConnectionManager.ListAllLabeledConnections()
	}

	me, err := libkb.LoadMe(libkb.NewLoadUserArg(h.G()))
	if err != nil {
		h.G().Log.Debug("| could not load me user")
	} else {
		device, err := me.GetComputedKeyFamily().GetCurrentDevice(h.G())
		if err != nil {
			h.G().Log.Debug("| GetCurrentDevice failed: %s", err)
		} else {
			res.Device = device.ProtExport()
		}
	}

	h.G().LoginState().Account(func(a *libkb.Account) {
		res.PassphraseStreamCached = a.PassphraseStreamCache().Valid()
		if a.LoginSession() != nil {
			res.Session = a.LoginSession().Status()
		}
	}, "ConfigHandler::GetExtendedStatus")

	current, all, err := h.G().GetAllUserNames()
	if err != nil {
		h.G().Log.Debug("| died in GetAllUseranmes()")
		return res, err
	}
	res.DefaultUsername = current.String()
	p := make([]string, len(all))
	for i, u := range all {
		p[i] = u.String()
	}
	res.ProvisionedUsernames = p
	res.PlatformInfo = getPlatformInfo()

	return res, nil
}

func (h ConfigHandler) GetConfig(_ context.Context, sessionID int) (keybase1.Config, error) {
	var c keybase1.Config

	c.ServerURI = h.G().Env.GetServerURI()
	c.RunMode = string(h.G().Env.GetRunMode())
	var err error
	c.SocketFile, err = h.G().Env.GetSocketFile()
	if err != nil {
		return c, err
	}

	gpg := h.G().GetGpgClient()
	canExec, err := gpg.CanExec()
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path()
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	}

	c.ConfigPath = h.G().Env.GetConfigFilename()
	c.Label = h.G().Env.GetLabel()
	if h.svc != nil {
		if h.svc.ForkType == keybase1.ForkType_AUTO {
			c.IsAutoForked = true
		}
		c.ForkType = h.svc.ForkType
	}

	return c, nil
}

func (h ConfigHandler) SetUserConfig(_ context.Context, arg keybase1.SetUserConfigArg) (err error) {
	eng := engine.NewUserConfigEngine(&engine.UserConfigEngineArg{
		Key:   arg.Key,
		Value: arg.Value,
	}, h.G())

	ctx := &engine.Context{}
	err = engine.RunEngine(eng, ctx)
	if err != nil {
		return err
	}
	return nil
}

func (h ConfigHandler) SetPath(_ context.Context, arg keybase1.SetPathArg) error {
	svcPath := os.Getenv("PATH")
	h.G().Log.Debug("SetPath: service path = %s", svcPath)
	h.G().Log.Debug("SetPath: client path =  %s", arg.Path)

	pathenv := strings.Split(svcPath, ":")
	pathset := make(map[string]bool)
	for _, p := range pathenv {
		pathset[p] = true
	}

	var clientAdditions []string
	for _, dir := range strings.Split(arg.Path, ":") {
		if _, ok := pathset[dir]; ok {
			continue
		}
		clientAdditions = append(clientAdditions, dir)
	}

	pathenv = append(pathenv, clientAdditions...)
	combined := strings.Join(pathenv, ":")

	if combined == svcPath {
		return nil
	}

	h.G().Log.Debug("SetPath: setting service path: %s", combined)
	os.Setenv("PATH", combined)

	return nil
}

func (h ConfigHandler) HelloIAm(_ context.Context, arg keybase1.ClientDetails) error {
	return h.G().ConnectionManager.Label(h.connID, arg)
}
<commit_msg>platform dependent path logic broke windows<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"golang.org/x/net/context"

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

type ConfigHandler struct {
	libkb.Contextified
	xp     rpc.Transporter
	svc    *Service
	connID libkb.ConnectionID
}

func NewConfigHandler(xp rpc.Transporter, i libkb.ConnectionID, g *libkb.GlobalContext, svc *Service) *ConfigHandler {
	return &ConfigHandler{
		Contextified: libkb.NewContextified(g),
		xp:           xp,
		svc:          svc,
		connID:       i,
	}
}

func (h ConfigHandler) GetCurrentStatus(_ context.Context, sessionID int) (res keybase1.GetCurrentStatusRes, err error) {
	var cs libkb.CurrentStatus
	if cs, err = libkb.GetCurrentStatus(h.G()); err == nil {
		res = cs.Export()
	}
	return
}

func getPlatformInfo() keybase1.PlatformInfo {
	return keybase1.PlatformInfo{
		Os:        runtime.GOOS,
		Arch:      runtime.GOARCH,
		GoVersion: runtime.Version(),
	}
}

func (h ConfigHandler) GetValue(_ context.Context, path string) (ret keybase1.ConfigValue, err error) {
	var i interface{}
	i, err = h.G().Env.GetConfig().GetInterfaceAtPath(path)
	if err != nil {
		return ret, err
	}
	if i == nil {
		ret.IsNull = true
	} else {
		switch v := i.(type) {
		case int:
			ret.I = &v
		case string:
			ret.S = &v
		case bool:
			ret.B = &v
		case float64:
			tmp := int(v)
			ret.I = &tmp
		default:
			var b []byte
			b, err = json.Marshal(v)
			if err == nil {
				tmp := string(b)
				ret.O = &tmp
			}
		}
	}
	return ret, err
}

func (h ConfigHandler) SetValue(_ context.Context, arg keybase1.SetValueArg) (err error) {
	w := h.G().Env.GetConfigWriter()
	if arg.Path == "users" {
		err = fmt.Errorf("The field 'users' cannot be edited for fear of config corruption")
		return err
	}
	switch {
	case arg.Value.IsNull:
		w.SetNullAtPath(arg.Path)
	case arg.Value.S != nil:
		w.SetStringAtPath(arg.Path, *arg.Value.S)
	case arg.Value.I != nil:
		w.SetIntAtPath(arg.Path, *arg.Value.I)
	case arg.Value.B != nil:
		w.SetBoolAtPath(arg.Path, *arg.Value.B)
	case arg.Value.O != nil:
		var jw *jsonw.Wrapper
		jw, err = jsonw.Unmarshal([]byte(*arg.Value.O))
		if err == nil {
			err = w.SetWrapperAtPath(arg.Path, jw)
		}
	default:
		err = fmt.Errorf("Bad type for setting a value")
	}
	if err == nil {
		h.G().ConfigReload()
	}
	return err
}

func (h ConfigHandler) ClearValue(_ context.Context, path string) error {
	h.G().Env.GetConfigWriter().DeleteAtPath(path)
	h.G().ConfigReload()
	return nil
}

func (h ConfigHandler) GetExtendedStatus(_ context.Context, sessionID int) (res keybase1.ExtendedStatus, err error) {
	defer h.G().Trace("ConfigHandler::GetExtendedStatus", func() error { return err })()

	res.Standalone = h.G().Env.GetStandalone()
	res.LogDir = h.G().Env.GetLogDir()

	// Should work in standalone mode too
	if h.G().ConnectionManager != nil {
		res.Clients = h.G().ConnectionManager.ListAllLabeledConnections()
	}

	me, err := libkb.LoadMe(libkb.NewLoadUserArg(h.G()))
	if err != nil {
		h.G().Log.Debug("| could not load me user")
	} else {
		device, err := me.GetComputedKeyFamily().GetCurrentDevice(h.G())
		if err != nil {
			h.G().Log.Debug("| GetCurrentDevice failed: %s", err)
		} else {
			res.Device = device.ProtExport()
		}
	}

	h.G().LoginState().Account(func(a *libkb.Account) {
		res.PassphraseStreamCached = a.PassphraseStreamCache().Valid()
		if a.LoginSession() != nil {
			res.Session = a.LoginSession().Status()
		}
	}, "ConfigHandler::GetExtendedStatus")

	current, all, err := h.G().GetAllUserNames()
	if err != nil {
		h.G().Log.Debug("| died in GetAllUseranmes()")
		return res, err
	}
	res.DefaultUsername = current.String()
	p := make([]string, len(all))
	for i, u := range all {
		p[i] = u.String()
	}
	res.ProvisionedUsernames = p
	res.PlatformInfo = getPlatformInfo()

	return res, nil
}

func (h ConfigHandler) GetConfig(_ context.Context, sessionID int) (keybase1.Config, error) {
	var c keybase1.Config

	c.ServerURI = h.G().Env.GetServerURI()
	c.RunMode = string(h.G().Env.GetRunMode())
	var err error
	c.SocketFile, err = h.G().Env.GetSocketFile()
	if err != nil {
		return c, err
	}

	gpg := h.G().GetGpgClient()
	canExec, err := gpg.CanExec()
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path()
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	}

	c.ConfigPath = h.G().Env.GetConfigFilename()
	c.Label = h.G().Env.GetLabel()
	if h.svc != nil {
		if h.svc.ForkType == keybase1.ForkType_AUTO {
			c.IsAutoForked = true
		}
		c.ForkType = h.svc.ForkType
	}

	return c, nil
}

func (h ConfigHandler) SetUserConfig(_ context.Context, arg keybase1.SetUserConfigArg) (err error) {
	eng := engine.NewUserConfigEngine(&engine.UserConfigEngineArg{
		Key:   arg.Key,
		Value: arg.Value,
	}, h.G())

	ctx := &engine.Context{}
	err = engine.RunEngine(eng, ctx)
	if err != nil {
		return err
	}
	return nil
}

func (h ConfigHandler) SetPath(_ context.Context, arg keybase1.SetPathArg) error {
	svcPath := os.Getenv("PATH")
	h.G().Log.Debug("SetPath: service path = %s", svcPath)
	h.G().Log.Debug("SetPath: client path =  %s", arg.Path)

	pathenv := filepath.SplitList(svcPath)
	pathset := make(map[string]bool)
	for _, p := range pathenv {
		pathset[p] = true
	}

	var clientAdditions []string
	for _, dir := range filepath.SplitList(arg.Path) {
		if _, ok := pathset[dir]; ok {
			continue
		}
		clientAdditions = append(clientAdditions, dir)
	}

	pathenv = append(pathenv, clientAdditions...)
	combined := strings.Join(pathenv, string(os.PathListSeparator))

	if combined == svcPath {
		return nil
	}

	h.G().Log.Debug("SetPath: setting service path: %s", combined)
	os.Setenv("PATH", combined)

	return nil
}

func (h ConfigHandler) HelloIAm(_ context.Context, arg keybase1.ClientDetails) error {
	return h.G().ConnectionManager.Label(h.connID, arg)
}
<|endoftext|>


<commit_before>package util

import ()

const (
	VERSION = "0.51"
)
<commit_msg>all in progress version will just be beta<commit_after>package util

import ()

const (
	VERSION = "0.52 beta"
)
<|endoftext|>


<commit_before>// Copyright 2011 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.

// +build dragonfly freebsd linux openbsd netbsd

package x509

import "io/ioutil"

// Possible certificate files; stop after finding one.
var certFiles = []string{
	"/etc/ssl/certs/ca-certificates.crt",     // Debian/Ubuntu/Gentoo etc.
	"/etc/pki/tls/certs/ca-bundle.crt",       // Fedora/RHEL
	"/etc/ssl/ca-bundle.pem",                 // OpenSUSE
	"/etc/ssl/cert.pem",                      // OpenBSD
	"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
}

func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
	return nil, nil
}

func initSystemRoots() {
	roots := NewCertPool()
	for _, file := range certFiles {
		data, err := ioutil.ReadFile(file)
		if err == nil {
			roots.AppendCertsFromPEM(data)
			systemRoots = roots
			return
		}
	}

	// All of the files failed to load. systemRoots will be nil which will
	// trigger a specific error at verification time.
}
<commit_msg>Add solaris to builds in root_unix.go (#1321)<commit_after>// Copyright 2011 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.

// +build dragonfly freebsd linux openbsd netbsd solaris

package x509

import "io/ioutil"

// Possible certificate files; stop after finding one.
var certFiles = []string{
	"/etc/ssl/certs/ca-certificates.crt",     // Debian/Ubuntu/Gentoo etc.
	"/etc/pki/tls/certs/ca-bundle.crt",       // Fedora/RHEL
	"/etc/ssl/ca-bundle.pem",                 // OpenSUSE
	"/etc/ssl/cert.pem",                      // OpenBSD
	"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
}

func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
	return nil, nil
}

func initSystemRoots() {
	roots := NewCertPool()
	for _, file := range certFiles {
		data, err := ioutil.ReadFile(file)
		if err == nil {
			roots.AppendCertsFromPEM(data)
			systemRoots = roots
			return
		}
	}

	// All of the files failed to load. systemRoots will be nil which will
	// trigger a specific error at verification time.
}
<|endoftext|>


<commit_before>package schedule

import (
	"strings"
	"testing"
	"time"
)

func TestGetRandomCommit(t *testing.T) {
	var tests = []struct{ day time.Time }{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC)},
	}
	messageBase := GetCommitMessageBase()
	for _, test := range tests {
		actual := GetRandomCommit(test.day, messageBase)
		if test.day.Day() != actual.DateTime.Day() {
			fmt := "GetRandomCommit should be on %v, but was %v"
			t.Errorf(fmt, test.day, actual.DateTime)
		}
		if len(actual.Message) < 0 {
			t.Error("GetRandomCommit should return a commit with a non empty message.")
		}
	}
}

func TestGetRandomNumber(t *testing.T) {
	var tests = []struct {
		min int
		max int
	}{
		{0, 0}, {0, 1}, {1, 1}, {0, 10}, {10, 20},
	}
	for _, test := range tests {
		actual := GetRandomNumber(test.min, test.max)
		if test.min > actual || actual > test.max {
			fmt := "GetRandomNumber(%d, %d) == %d; not min <= actual <= max"
			t.Errorf(fmt, test.min, test.max, actual)
		}
	}
}

func TestGetRandomCommitMessage(t *testing.T) {
	var tests = []struct{ length int }{{1}, {2}, {4}, {8}}
	messageBase := GetCommitMessageBase()
	for _, test := range tests {
		actual := getRandomCommitMessage(messageBase, test.length)
		actualLength := len(strings.Split(actual, " "))
		t.Logf("Message: %s (length: %d)", actual, actualLength)
		if actualLength < 0 || test.length < actualLength {
			fmt := "getRandomCommitMessage(%d) == %s (length: %d)"
			t.Errorf(fmt, test.length, actual, actualLength)
		}
	}
}

func TestGetRandomTime(t *testing.T) {
	var tests = []struct {
		date time.Time
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC)},
	}
	for _, test := range tests {
		actual := getRandomTime(test.date)
		t.Logf("Date: %v and date with random time: %v", test.date, actual)
		dayBefore := test.date.AddDate(0, 0, -1)
		dayAfter := test.date.AddDate(0, 0, 1)
		if actual.After(dayAfter) || actual.Before(dayBefore) {
			fmt := "getRandomTime(%v) == %v; not dayBefore < actual < dayAfter"
			t.Errorf(fmt, test.date, actual)
		}
	}
}

func TestGenerateRandomCommits(t *testing.T) {
	var tests = []struct {
		date       time.Time
		numCommits int
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC), 1},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC), 2},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC), 8},
	}
	messageBase := GetCommitMessageBase()
	for _, test := range tests {
		actual := GenerateRandomCommits(test.date, test.numCommits, messageBase)
		commitCount := 0
		for commit := range actual {
			if commit.DateTime.Day() != test.date.Day() {
				t.Error("Commit has a wrong date")
			}
			commitCount++
		}
		if commitCount != test.numCommits {
			t.Errorf("Expected %d commits, but got %d", test.numCommits, commitCount)
		}
	}
}
<commit_msg>Fix test, minimum words has changed.<commit_after>package schedule

import (
	"strings"
	"testing"
	"time"
)

func TestGetRandomCommit(t *testing.T) {
	var tests = []struct{ day time.Time }{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC)},
	}
	messageBase := GetCommitMessageBase()
	for _, test := range tests {
		actual := GetRandomCommit(test.day, messageBase)
		if test.day.Day() != actual.DateTime.Day() {
			fmt := "GetRandomCommit should be on %v, but was %v"
			t.Errorf(fmt, test.day, actual.DateTime)
		}
		if len(actual.Message) < 0 {
			t.Error("GetRandomCommit should return a commit with a non empty message.")
		}
	}
}

func TestGetRandomNumber(t *testing.T) {
	var tests = []struct {
		min int
		max int
	}{
		{0, 0}, {0, 1}, {1, 1}, {0, 10}, {10, 20},
	}
	for _, test := range tests {
		actual := GetRandomNumber(test.min, test.max)
		if test.min > actual || actual > test.max {
			fmt := "GetRandomNumber(%d, %d) == %d; not min <= actual <= max"
			t.Errorf(fmt, test.min, test.max, actual)
		}
	}
}

func TestGetRandomCommitMessage(t *testing.T) {
	var tests = []struct{ length int }{{2}, {4}, {8}}
	messageBase := GetCommitMessageBase()
	for _, test := range tests {
		actual := getRandomCommitMessage(messageBase, test.length)
		actualLength := len(strings.Split(actual, " "))
		t.Logf("Message: %s (length: %d)", actual, actualLength)
		if actualLength < 0 || test.length < actualLength {
			fmt := "getRandomCommitMessage(%d) == %s (length: %d)"
			t.Errorf(fmt, test.length, actual, actualLength)
		}
	}
}

func TestGetRandomTime(t *testing.T) {
	var tests = []struct {
		date time.Time
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC)},
	}
	for _, test := range tests {
		actual := getRandomTime(test.date)
		t.Logf("Date: %v and date with random time: %v", test.date, actual)
		dayBefore := test.date.AddDate(0, 0, -1)
		dayAfter := test.date.AddDate(0, 0, 1)
		if actual.After(dayAfter) || actual.Before(dayBefore) {
			fmt := "getRandomTime(%v) == %v; not dayBefore < actual < dayAfter"
			t.Errorf(fmt, test.date, actual)
		}
	}
}

func TestGenerateRandomCommits(t *testing.T) {
	var tests = []struct {
		date       time.Time
		numCommits int
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC), 1},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC), 2},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC), 8},
	}
	messageBase := GetCommitMessageBase()
	for _, test := range tests {
		actual := GenerateRandomCommits(test.date, test.numCommits, messageBase)
		commitCount := 0
		for commit := range actual {
			if commit.DateTime.Day() != test.date.Day() {
				t.Error("Commit has a wrong date")
			}
			commitCount++
		}
		if commitCount != test.numCommits {
			t.Errorf("Expected %d commits, but got %d", test.numCommits, commitCount)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa-logfmt command
// Author: arnej

package main

import (
	"github.com/spf13/cobra"
	"github.com/vespa-engine/vespa/client/go/build"
	"github.com/vespa-engine/vespa/client/go/cmd/logfmt"
)

func NewLogfmtCmd() *cobra.Command {
	var (
		curOptions logfmt.Options = logfmt.NewOptions()
	)
	cmd := &cobra.Command{
		Use:   "vespa-logfmt",
		Short: "convert vespa.log to human-readable format",
		Long: `vespa-logfmt takes input in the internal vespa format
and converts it to something human-readable`,
		Version: build.Version,
		Run: func(cmd *cobra.Command, args []string) {
			logfmt.RunLogfmt(&curOptions, args)
		},
		Args: cobra.MaximumNArgs(1),
	}
	cmd.Flags().VarP(&curOptions.ShowLevels, "level", "l", "turn levels on/off\n")
	cmd.Flags().VarP(&curOptions.ShowFields, "show", "s", "turn fields shown on/off\n")
	cmd.Flags().VarP(&curOptions.ComponentFilter, "component", "c", "select components by regexp")
	cmd.Flags().VarP(&curOptions.MessageFilter, "message", "m", "select messages by regexp")
	cmd.Flags().BoolVarP(&curOptions.OnlyInternal, "internal", "i", false, "select only internal components")
	cmd.Flags().BoolVar(&curOptions.TruncateService, "truncateservice", false, "truncate service name")
	cmd.Flags().BoolVar(&curOptions.TruncateService, "ts", false, "")
	cmd.Flags().BoolVarP(&curOptions.FollowTail, "follow", "f", false, "follow logfile with tail -f")
	cmd.Flags().BoolVarP(&curOptions.DequoteNewlines, "nldequote", "N", false, "dequote newlines embedded in message")
	cmd.Flags().BoolVarP(&curOptions.TruncateComponent, "truncatecomponent", "t", false, "truncate component name")
	cmd.Flags().BoolVar(&curOptions.TruncateComponent, "tc", false, "")
	cmd.Flags().StringVarP(&curOptions.OnlyHostname, "host", "H", "", "select only one host")
	cmd.Flags().StringVarP(&curOptions.OnlyPid, "pid", "p", "", "select only one process ID")
	cmd.Flags().StringVarP(&curOptions.OnlyService, "service", "S", "", "select only one service")
	cmd.Flags().MarkHidden("tc")
	cmd.Flags().MarkHidden("ts")
	return cmd
}
<commit_msg>allow lowercase 'n' for newline dequoting<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa-logfmt command
// Author: arnej

package main

import (
	"github.com/spf13/cobra"
	"github.com/vespa-engine/vespa/client/go/build"
	"github.com/vespa-engine/vespa/client/go/cmd/logfmt"
)

func NewLogfmtCmd() *cobra.Command {
	var (
		curOptions logfmt.Options = logfmt.NewOptions()
	)
	cmd := &cobra.Command{
		Use:   "vespa-logfmt",
		Short: "convert vespa.log to human-readable format",
		Long: `vespa-logfmt takes input in the internal vespa format
and converts it to something human-readable`,
		Version: build.Version,
		Run: func(cmd *cobra.Command, args []string) {
			logfmt.RunLogfmt(&curOptions, args)
		},
		Args: cobra.MaximumNArgs(1),
	}
	cmd.Flags().VarP(&curOptions.ShowLevels, "level", "l", "turn levels on/off\n")
	cmd.Flags().VarP(&curOptions.ShowFields, "show", "s", "turn fields shown on/off\n")
	cmd.Flags().VarP(&curOptions.ComponentFilter, "component", "c", "select components by regexp")
	cmd.Flags().VarP(&curOptions.MessageFilter, "message", "m", "select messages by regexp")
	cmd.Flags().BoolVarP(&curOptions.OnlyInternal, "internal", "i", false, "select only internal components")
	cmd.Flags().BoolVar(&curOptions.TruncateService, "truncateservice", false, "truncate service name")
	cmd.Flags().BoolVar(&curOptions.TruncateService, "ts", false, "")
	cmd.Flags().BoolVarP(&curOptions.FollowTail, "follow", "f", false, "follow logfile with tail -f")
	cmd.Flags().BoolVarP(&curOptions.DequoteNewlines, "nldequote", "N", false, "dequote newlines embedded in message")
	cmd.Flags().BoolVarP(&curOptions.DequoteNewlines, "dequotenewlines", "n", false, "dequote newlines embedded in message")
	cmd.Flags().BoolVarP(&curOptions.TruncateComponent, "truncatecomponent", "t", false, "truncate component name")
	cmd.Flags().BoolVar(&curOptions.TruncateComponent, "tc", false, "")
	cmd.Flags().StringVarP(&curOptions.OnlyHostname, "host", "H", "", "select only one host")
	cmd.Flags().StringVarP(&curOptions.OnlyPid, "pid", "p", "", "select only one process ID")
	cmd.Flags().StringVarP(&curOptions.OnlyService, "service", "S", "", "select only one service")
	cmd.Flags().MarkHidden("tc")
	cmd.Flags().MarkHidden("ts")
	cmd.Flags().MarkHidden("dequotenewlines")
	return cmd
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Space Monkey, Inc.

package time

import (
    "time"
)

func Monotonic() time.Duration {
    sec, nsec := monotime()
    return time.Duration(sec * 1000000000 + int64(nsec))
}
<commit_msg>space monkey internal commit export<commit_after>// Copyright (C) 2014 Space Monkey, Inc.

package time

import (
    "time"
)

func Monotonic() time.Duration {
    sec, nsec := monotime()
    return time.Duration(sec*1000000000 + int64(nsec))
}
<|endoftext|>