<commit_before>package termite

import (
	"github.com/hanwen/go-fuse/fuse"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"rpc"
	"sort"
	"strings"
)

type Master struct {
	cache         *ContentCache
	fileServer    *FsServer
	fileServerRpc *rpc.Server
	secret        []byte

	*localDecider

	stats          *masterStats
	retryCount     int
	mirrors        *mirrorConnections
	localRpcServer *rpc.Server
	localServer    *LocalMaster
	writableRoot   string
	srcRoot        string
	pending        *PendingConnections
}

func NewMaster(cache *ContentCache, coordinator string, workers []string, secret []byte, excluded []string, maxJobs int) *Master {
	me := &Master{
		cache:      cache,
		fileServer: NewFsServer("/", cache, excluded),
		secret:     secret,
		retryCount: 3,
		stats:      newMasterStats(),
	}
	me.fileServer.multiplyPaths = func(n string) []string { return me.multiplyPaths(n) }
	me.mirrors = newMirrorConnections(me, workers, coordinator, maxJobs)
	me.localServer = &LocalMaster{me}
	me.secret = secret
	me.pending = NewPendingConnections()
	me.fileServerRpc = rpc.NewServer()
	me.fileServerRpc.Register(me.fileServer)
	me.localRpcServer = rpc.NewServer()
	me.localRpcServer.Register(me.localServer)

	return me
}

// TODO - write e2e test for this.
func (me *Master) multiplyPaths(name string) []string {
	names := []string{name}
	// TODO - cleanpath.
	if strings.HasPrefix(name, me.writableRoot) && me.srcRoot != "" {
		names = append(names, me.srcRoot+name[len(me.writableRoot):])
	}
	for _, n := range names {
		// TODO - configurable
		if strings.HasSuffix(n, ".gch") {
			names = append(names, n[:len(n)-len(".gch")])
		}
	}
	if len(names) > 1 {
		log.Println("multiplied", names)
	}
	return names
}

func (me *Master) SetSrcRoot(root string) {
	root, _ = filepath.Abs(root)
	me.srcRoot = filepath.Clean(root)
	log.Println("SrcRoot is", me.srcRoot)
}

func (me *Master) SetKeepAlive(seconds float64) {
	if seconds > 0 {
		me.mirrors.keepAliveNs = int64(1e9 * seconds)
	}
}

func (me *Master) Start(sock string) {
	absSock, err := filepath.Abs(sock)
	if err != nil {
		log.Fatal("abs", err)
	}

	fi, _ := os.Stat(absSock)
	if fi != nil && fi.IsSocket() {
		conn, _ := net.Dial("unix", absSock)
		if conn != nil {
			conn.Close()
			log.Fatal("socket has someone listening: ", absSock)
		}
		// TODO - should check explicitly for the relevant error message.
		log.Println("removing dead socket", absSock)
		os.Remove(absSock)
	}

	listener, err := net.Listen("unix", absSock)
	defer os.Remove(absSock)
	if err != nil {
		log.Fatal("startLocalServer: ", err)
	}
	err = os.Chmod(absSock, 0700)
	if err != nil {
		log.Fatal("sock chmod", err)
	}

	me.writableRoot, err = filepath.EvalSymlinks(absSock)
	if err != nil {
		log.Fatal("EvalSymlinks", err)
	}
	me.writableRoot = filepath.Clean(me.writableRoot)
	me.writableRoot, _ = filepath.Split(me.writableRoot)
	me.writableRoot = filepath.Clean(me.writableRoot)

	log.Println("accepting connections on", absSock)
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("listener.accept", err)
		}
		if !me.pending.Accept(conn) {
			go me.localRpcServer.ServeConn(conn)
		}
	}
}

func (me *Master) createMirror(addr string, jobs int) (*mirrorConnection, os.Error) {
	conn, err := DialTypedConnection(addr, RPC_CHANNEL, me.secret)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	rpcId := ConnectionId()
	rpcConn, err := DialTypedConnection(addr, rpcId, me.secret)
	if err != nil {
		return nil, err
	}

	revId := ConnectionId()
	revConn, err := DialTypedConnection(addr, revId, me.secret)
	if err != nil {
		rpcConn.Close()
		return nil, err
	}

	req := CreateMirrorRequest{
		RpcId:        rpcId,
		RevRpcId:     revId,
		WritableRoot: me.writableRoot,
		MaxJobCount:  jobs,
	}
	rep := CreateMirrorResponse{}
	cl := rpc.NewClient(conn)
	err = cl.Call("WorkerDaemon.CreateMirror", &req, &rep)

	if err != nil {
		revConn.Close()
		rpcConn.Close()
		return nil, err
	}

	go me.fileServerRpc.ServeConn(revConn)

	mc := &mirrorConnection{
		rpcClient:     rpc.NewClient(rpcConn),
		connection:    rpcConn,
		maxJobs:       rep.GrantedJobCount,
		availableJobs: rep.GrantedJobCount,
	}

	mc.queueFiles(me.fileServer.copyCache())

	return mc, nil
}

func (me *Master) runOnMirror(mirror *mirrorConnection, req *WorkRequest, rep *WorkReply) os.Error {
	defer me.mirrors.jobDone(mirror)

	// Tunnel stdin.
	if req.StdinId != "" {
		inputConn := me.pending.WaitConnection(req.StdinId)
		destInputConn, err := DialTypedConnection(mirror.connection.RemoteAddr().String(),
			req.StdinId, me.secret)
		if err != nil {
			return err
		}
		go func() {
			HookedCopy(destInputConn, inputConn, PrintStdinSliceLen)
			destInputConn.Close()
			inputConn.Close()
		}()
	}

	log.Println("Running command", req.Argv)
	if req.Debug {
		log.Println("with environment", req.Env)
	}

	err := mirror.rpcClient.Call("Mirror.Run", &req, &rep)
	return err
}

func (me *Master) prefetchFiles(req *WorkRequest) {
	files := map[string]int{}
	for _, arg := range req.Argv {
		for _, root := range []string{me.srcRoot, me.writableRoot} {
			for _, f := range DetectFiles(root, arg) {
				files[f] = 1
			}
		}
	}

	for f, _ := range files {
		a := FileAttr{}
		me.fileServer.oneGetAttr(f, &a)
		req.Prefetch = append(req.Prefetch, a)
	}
	if len(req.Prefetch) > 0 {
		log.Println("Prefetch", req.Prefetch)
	}
}

func (me *Master) runOnce(req *WorkRequest, rep *WorkReply) os.Error {
	localRep := *rep
	mirror, err := me.mirrors.pick()
	if err != nil {
		return err
	}
	err = mirror.sendFiles()
	if err != nil {
		me.mirrors.drop(mirror, err)
		return err
	}

	me.prefetchFiles(req)
	err = me.runOnMirror(mirror, req, &localRep)
	if err != nil {
		me.mirrors.drop(mirror, err)
		return err
	}

	err = me.replayFileModifications(mirror.rpcClient, localRep.Files)
	me.fileServer.updateFiles(localRep.Files)
	if err != nil {
		return err
	}
	*rep = localRep
	rep.Files = nil

	me.mirrors.queueFiles(mirror, localRep.Files)
	return err
}

func (me *Master) run(req *WorkRequest, rep *WorkReply) (err os.Error) {
	me.stats.MarkReceive()

	err = me.runOnce(req, rep)
	for i := 0; i < me.retryCount && err != nil; i++ {
		log.Println("Retrying; last error:", err)
		err = me.runOnce(req, rep)
	}

	me.stats.MarkReturn()
	return err
}

func (me *Master) replayFileModifications(worker *rpc.Client, infos []FileAttr) os.Error {
	// Must get data before we modify the file-system, so we don't
	// leave the FS in a half-finished state.
	for _, info := range infos {
		if info.Hash != "" && info.Content == nil {
			// TODO - stream directly from network connection to file.
			err := FetchBetweenContentServers(
				worker, "Mirror.FileContent", info.FileInfo.Size, info.Hash,
				me.cache)
			if err != nil {
				return err
			}
		}
	}

	entries := make(map[string]*FileAttr)
	names := []string{}
	for i, info := range infos {
		names = append(names, info.Path)
		entries[info.Path] = &infos[i]
	}

	deletes := []string{}
	// Sort so we get parents before children.
	sort.Strings(names)
	for _, name := range names {
		info := entries[name]
		var err os.Error

		// TODO - deletion test.
		logStr := ""
		if info.FileInfo != nil && info.FileInfo.IsDirectory() {
			if name == "" {
				name = "/"
			}
			err = os.Mkdir(name, info.FileInfo.Mode&07777)
			if err != nil {
				// some other process may have created
				// the dir.
				if fi, _ := os.Lstat(name); fi != nil && fi.IsDirectory() {
					err = nil
				}
			}
		}
		if info.Hash != "" {
			log.Printf("Replay file content %s %x", name, info.Hash)
			content := info.Content

			if content == nil {
				err = CopyFile(info.Path, me.cache.Path(info.Hash), int(info.FileInfo.Mode))
				logStr += "CopyFile,"
			} else {
				me.cache.Save(content)
				err = ioutil.WriteFile(info.Path, content, info.FileInfo.Mode&07777)
				logStr += "WriteFile,"
			}
			if err == nil {
				err = os.Chtimes(info.Path, info.FileInfo.Atime_ns, info.FileInfo.Mtime_ns)
				logStr += "Chtimes,"
			}
		}
		if info.Link != "" {
			os.Remove(info.Path) // ignore error.
			err = os.Symlink(info.Link, info.Path)
			logStr += "Symlink,"
		}
		if !info.Status.Ok() {
			if info.Status == fuse.ENOENT {
				deletes = append(deletes, info.Path)
			} else {
				log.Fatal("Unknown status for replay", info.Status)
			}
		}

		if err != nil {
			log.Fatal("Replay error ", info.Path, " ", err, infos, logStr)
		}
	}

	// Must do deletes in reverse: children before parents.
	for i, _ := range deletes {
		d := deletes[len(deletes)-1-i]
		// TODO - should probably drop entries below d as well
		// if d is a directory.
		if err := os.RemoveAll(d); err != nil {
			log.Fatal("delete replay: ", err)
		}
	}
	return nil
}

func (me *Master) refreshAttributeCache() {
	for _, r := range []string{me.writableRoot, me.srcRoot} {
		updated := me.fileServer.refreshAttributeCache(r)
		me.mirrors.queueFiles(nil, updated)
	}
}

////////////////

// Expose functionality for the local tool to use.
type LocalMaster struct {
	master *Master
}

func (me *LocalMaster) Run(req *WorkRequest, rep *WorkReply) os.Error {
	if req.RanLocally {
		log.Println("Ran command locally:", req.Argv)
		return nil
	}

	return me.master.run(req, rep)
}

func (me *LocalMaster) RefreshAttributeCache(input *int, output *int) os.Error {
	log.Println("Refreshing attribute cache")
	me.master.refreshAttributeCache()
	log.Println("Refresh done")
	return nil
}
<commit_msg>multiplyPaths: Don't add srcRoot if equal to writableRoot.<commit_after>package termite

import (
	"github.com/hanwen/go-fuse/fuse"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"rpc"
	"sort"
	"strings"
)

type Master struct {
	cache         *ContentCache
	fileServer    *FsServer
	fileServerRpc *rpc.Server
	secret        []byte

	*localDecider

	stats          *masterStats
	retryCount     int
	mirrors        *mirrorConnections
	localRpcServer *rpc.Server
	localServer    *LocalMaster
	writableRoot   string
	srcRoot        string
	pending        *PendingConnections
}

func NewMaster(cache *ContentCache, coordinator string, workers []string, secret []byte, excluded []string, maxJobs int) *Master {
	me := &Master{
		cache:      cache,
		fileServer: NewFsServer("/", cache, excluded),
		secret:     secret,
		retryCount: 3,
		stats:      newMasterStats(),
	}
	me.fileServer.multiplyPaths = func(n string) []string { return me.multiplyPaths(n) }
	me.mirrors = newMirrorConnections(me, workers, coordinator, maxJobs)
	me.localServer = &LocalMaster{me}
	me.secret = secret
	me.pending = NewPendingConnections()
	me.fileServerRpc = rpc.NewServer()
	me.fileServerRpc.Register(me.fileServer)
	me.localRpcServer = rpc.NewServer()
	me.localRpcServer.Register(me.localServer)

	return me
}

// TODO - write e2e test for this.
func (me *Master) multiplyPaths(name string) []string {
	names := []string{name}
	// TODO - cleanpath.
	if strings.HasPrefix(name, me.writableRoot) && me.srcRoot != "" &&
		me.srcRoot != me.writableRoot {
		names = append(names, me.srcRoot+name[len(me.writableRoot):])
	}
	for _, n := range names {
		// TODO - configurable
		if strings.HasSuffix(n, ".gch") {
			names = append(names, n[:len(n)-len(".gch")])
		}
	}
	if len(names) > 1 {
		log.Println("multiplied", names)
	}
	return names
}

func (me *Master) SetSrcRoot(root string) {
	root, _ = filepath.Abs(root)
	me.srcRoot = filepath.Clean(root)
	log.Println("SrcRoot is", me.srcRoot)
}

func (me *Master) SetKeepAlive(seconds float64) {
	if seconds > 0 {
		me.mirrors.keepAliveNs = int64(1e9 * seconds)
	}
}

func (me *Master) Start(sock string) {
	absSock, err := filepath.Abs(sock)
	if err != nil {
		log.Fatal("abs", err)
	}

	fi, _ := os.Stat(absSock)
	if fi != nil && fi.IsSocket() {
		conn, _ := net.Dial("unix", absSock)
		if conn != nil {
			conn.Close()
			log.Fatal("socket has someone listening: ", absSock)
		}
		// TODO - should check explicitly for the relevant error message.
		log.Println("removing dead socket", absSock)
		os.Remove(absSock)
	}

	listener, err := net.Listen("unix", absSock)
	defer os.Remove(absSock)
	if err != nil {
		log.Fatal("startLocalServer: ", err)
	}
	err = os.Chmod(absSock, 0700)
	if err != nil {
		log.Fatal("sock chmod", err)
	}

	me.writableRoot, err = filepath.EvalSymlinks(absSock)
	if err != nil {
		log.Fatal("EvalSymlinks", err)
	}
	me.writableRoot = filepath.Clean(me.writableRoot)
	me.writableRoot, _ = filepath.Split(me.writableRoot)
	me.writableRoot = filepath.Clean(me.writableRoot)

	log.Println("accepting connections on", absSock)
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("listener.accept", err)
		}
		if !me.pending.Accept(conn) {
			go me.localRpcServer.ServeConn(conn)
		}
	}
}

func (me *Master) createMirror(addr string, jobs int) (*mirrorConnection, os.Error) {
	conn, err := DialTypedConnection(addr, RPC_CHANNEL, me.secret)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	rpcId := ConnectionId()
	rpcConn, err := DialTypedConnection(addr, rpcId, me.secret)
	if err != nil {
		return nil, err
	}

	revId := ConnectionId()
	revConn, err := DialTypedConnection(addr, revId, me.secret)
	if err != nil {
		rpcConn.Close()
		return nil, err
	}

	req := CreateMirrorRequest{
		RpcId:        rpcId,
		RevRpcId:     revId,
		WritableRoot: me.writableRoot,
		MaxJobCount:  jobs,
	}
	rep := CreateMirrorResponse{}
	cl := rpc.NewClient(conn)
	err = cl.Call("WorkerDaemon.CreateMirror", &req, &rep)

	if err != nil {
		revConn.Close()
		rpcConn.Close()
		return nil, err
	}

	go me.fileServerRpc.ServeConn(revConn)

	mc := &mirrorConnection{
		rpcClient:     rpc.NewClient(rpcConn),
		connection:    rpcConn,
		maxJobs:       rep.GrantedJobCount,
		availableJobs: rep.GrantedJobCount,
	}

	mc.queueFiles(me.fileServer.copyCache())

	return mc, nil
}

func (me *Master) runOnMirror(mirror *mirrorConnection, req *WorkRequest, rep *WorkReply) os.Error {
	defer me.mirrors.jobDone(mirror)

	// Tunnel stdin.
	if req.StdinId != "" {
		inputConn := me.pending.WaitConnection(req.StdinId)
		destInputConn, err := DialTypedConnection(mirror.connection.RemoteAddr().String(),
			req.StdinId, me.secret)
		if err != nil {
			return err
		}
		go func() {
			HookedCopy(destInputConn, inputConn, PrintStdinSliceLen)
			destInputConn.Close()
			inputConn.Close()
		}()
	}

	log.Println("Running command", req.Argv)
	if req.Debug {
		log.Println("with environment", req.Env)
	}

	err := mirror.rpcClient.Call("Mirror.Run", &req, &rep)
	return err
}

func (me *Master) prefetchFiles(req *WorkRequest) {
	files := map[string]int{}
	for _, arg := range req.Argv {
		for _, root := range []string{me.srcRoot, me.writableRoot} {
			for _, f := range DetectFiles(root, arg) {
				files[f] = 1
			}
		}
	}

	for f, _ := range files {
		a := FileAttr{}
		me.fileServer.oneGetAttr(f, &a)
		req.Prefetch = append(req.Prefetch, a)
	}
	if len(req.Prefetch) > 0 {
		log.Println("Prefetch", req.Prefetch)
	}
}

func (me *Master) runOnce(req *WorkRequest, rep *WorkReply) os.Error {
	localRep := *rep
	mirror, err := me.mirrors.pick()
	if err != nil {
		return err
	}
	err = mirror.sendFiles()
	if err != nil {
		me.mirrors.drop(mirror, err)
		return err
	}

	me.prefetchFiles(req)
	err = me.runOnMirror(mirror, req, &localRep)
	if err != nil {
		me.mirrors.drop(mirror, err)
		return err
	}

	err = me.replayFileModifications(mirror.rpcClient, localRep.Files)
	me.fileServer.updateFiles(localRep.Files)
	if err != nil {
		return err
	}
	*rep = localRep
	rep.Files = nil

	me.mirrors.queueFiles(mirror, localRep.Files)
	return err
}

func (me *Master) run(req *WorkRequest, rep *WorkReply) (err os.Error) {
	me.stats.MarkReceive()

	err = me.runOnce(req, rep)
	for i := 0; i < me.retryCount && err != nil; i++ {
		log.Println("Retrying; last error:", err)
		err = me.runOnce(req, rep)
	}

	me.stats.MarkReturn()
	return err
}

func (me *Master) replayFileModifications(worker *rpc.Client, infos []FileAttr) os.Error {
	// Must get data before we modify the file-system, so we don't
	// leave the FS in a half-finished state.
	for _, info := range infos {
		if info.Hash != "" && info.Content == nil {
			// TODO - stream directly from network connection to file.
			err := FetchBetweenContentServers(
				worker, "Mirror.FileContent", info.FileInfo.Size, info.Hash,
				me.cache)
			if err != nil {
				return err
			}
		}
	}

	entries := make(map[string]*FileAttr)
	names := []string{}
	for i, info := range infos {
		names = append(names, info.Path)
		entries[info.Path] = &infos[i]
	}

	deletes := []string{}
	// Sort so we get parents before children.
	sort.Strings(names)
	for _, name := range names {
		info := entries[name]
		var err os.Error

		// TODO - deletion test.
		logStr := ""
		if info.FileInfo != nil && info.FileInfo.IsDirectory() {
			if name == "" {
				name = "/"
			}
			err = os.Mkdir(name, info.FileInfo.Mode&07777)
			if err != nil {
				// some other process may have created
				// the dir.
				if fi, _ := os.Lstat(name); fi != nil && fi.IsDirectory() {
					err = nil
				}
			}
		}
		if info.Hash != "" {
			log.Printf("Replay file content %s %x", name, info.Hash)
			content := info.Content

			if content == nil {
				err = CopyFile(info.Path, me.cache.Path(info.Hash), int(info.FileInfo.Mode))
				logStr += "CopyFile,"
			} else {
				me.cache.Save(content)
				err = ioutil.WriteFile(info.Path, content, info.FileInfo.Mode&07777)
				logStr += "WriteFile,"
			}
			if err == nil {
				err = os.Chtimes(info.Path, info.FileInfo.Atime_ns, info.FileInfo.Mtime_ns)
				logStr += "Chtimes,"
			}
		}
		if info.Link != "" {
			os.Remove(info.Path) // ignore error.
			err = os.Symlink(info.Link, info.Path)
			logStr += "Symlink,"
		}
		if !info.Status.Ok() {
			if info.Status == fuse.ENOENT {
				deletes = append(deletes, info.Path)
			} else {
				log.Fatal("Unknown status for replay", info.Status)
			}
		}

		if err != nil {
			log.Fatal("Replay error ", info.Path, " ", err, infos, logStr)
		}
	}

	// Must do deletes in reverse: children before parents.
	for i, _ := range deletes {
		d := deletes[len(deletes)-1-i]
		// TODO - should probably drop entries below d as well
		// if d is a directory.
		if err := os.RemoveAll(d); err != nil {
			log.Fatal("delete replay: ", err)
		}
	}
	return nil
}

func (me *Master) refreshAttributeCache() {
	for _, r := range []string{me.writableRoot, me.srcRoot} {
		updated := me.fileServer.refreshAttributeCache(r)
		me.mirrors.queueFiles(nil, updated)
	}
}

////////////////

// Expose functionality for the local tool to use.
type LocalMaster struct {
	master *Master
}

func (me *LocalMaster) Run(req *WorkRequest, rep *WorkReply) os.Error {
	if req.RanLocally {
		log.Println("Ran command locally:", req.Argv)
		return nil
	}

	return me.master.run(req, rep)
}

func (me *LocalMaster) RefreshAttributeCache(input *int, output *int) os.Error {
	log.Println("Refreshing attribute cache")
	me.master.refreshAttributeCache()
	log.Println("Refresh done")
	return nil
}
<|endoftext|>


<commit_before>package etcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"
)

var (
	curlChan chan string
)

// SetCurlChan sets a channel to which cURL commands which can be used to
// re-produce requests are sent.  This is useful for debugging.
func SetCurlChan(c chan string) {
	curlChan = c
}

// get issues a GET request
func (c *Client) get(key string, options options) (*Response, error) {
	logger.Debugf("get %s [%s]", key, c.cluster.Leader)

	p := path.Join("keys", key)
	// If consistency level is set to STRONG, append
	// the `consistent` query string.
	if c.config.Consistency == STRONG_CONSISTENCY {
		options["consistent"] = true
	}

	str, err := options.toParameters(VALID_GET_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	resp, err := c.sendRequest("GET", p, nil)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// put issues a PUT request
func (c *Client) put(key string, value string, ttl uint64, options options) (*Response, error) {
	logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
	p := path.Join("keys", key)

	str, err := options.toParameters(VALID_PUT_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	resp, err := c.sendRequest("PUT", p, buildValues(value, ttl))

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// post issues a POST request
func (c *Client) post(key string, value string, ttl uint64) (*Response, error) {
	logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
	p := path.Join("keys", key)

	resp, err := c.sendRequest("POST", p, buildValues(value, ttl))

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// delete issues a DELETE request
func (c *Client) delete(key string, options options) (*Response, error) {
	logger.Debugf("delete %s [%s]", key, c.cluster.Leader)

	p := path.Join("keys", key)

	str, err := options.toParameters(VALID_DELETE_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	resp, err := c.sendRequest("DELETE", p, nil)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// sendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) sendRequest(method string, _path string, values url.Values) (*Response, error) {
	var resp *http.Response
	var req *http.Request

	trial := 0

	// if we connect to a follower, we will retry until we found a leader
	for {
		var httpPath string

		trial++
		if trial > 2*len(c.cluster.Machines) {
			return nil, fmt.Errorf("Cannot reach servers after %v time", trial)
		}

		u, err := url.Parse(_path)
		if err != nil {
			return nil, err
		}

		// If _path has schema already, then it's assumed to be
		// a complete URL and therefore needs no further processing.
		if u.Scheme != "" {
			httpPath = _path
		} else {
			if method == "GET" && c.config.Consistency == WEAK_CONSISTENCY {
				// If it's a GET and consistency level is set to WEAK,
				// then use a random machine.
				httpPath = c.getHttpPath(true, _path)
			} else {
				// Else use the leader.
				httpPath = c.getHttpPath(false, _path)
			}
		}

		// Return a cURL command if curlChan is set
		if curlChan != nil {
			command := fmt.Sprintf("curl -X %s %s", method, httpPath)
			for key, value := range values {
				command += fmt.Sprintf(" -d %s=%s", key, value[0])
			}
			curlChan <- command
		}

		logger.Debug("send.request.to ", httpPath, " | method ", method)

		if values == nil {
			req, _ = http.NewRequest(method, httpPath, nil)
		} else {
			req, _ = http.NewRequest(method, httpPath,
				strings.NewReader(values.Encode()))

			req.Header.Set("Content-Type",
				"application/x-www-form-urlencoded; param=value")
		}

		resp, err = c.httpClient.Do(req)

		// network error, change a machine!
		if err != nil {
			c.switchLeader(trial % len(c.cluster.Machines))
			time.Sleep(time.Millisecond * 200)
			continue
		}

		if resp != nil {
			logger.Debug("recv.response.from ", httpPath)

			if resp.StatusCode == http.StatusTemporaryRedirect {
				httpPath := resp.Header.Get("Location")

				resp.Body.Close()

				if httpPath == "" {
					return nil, errors.New("Cannot get redirection location")
				}

				c.updateLeader(httpPath)
				logger.Debug("send.redirect")
				// try to connect the leader
				continue
			} else if resp.StatusCode == http.StatusInternalServerError {
				resp.Body.Close()
				time.Sleep(time.Millisecond * 200)
				continue
			} else {
				logger.Debug("send.return.response ", httpPath)
				break
			}

		}

		logger.Debug("error.from ", httpPath, " ", err.Error())
		return nil, err
	}

	// Convert HTTP response to etcd response
	b, err := ioutil.ReadAll(resp.Body)

	resp.Body.Close()

	if err != nil {
		return nil, err
	}

	if !(resp.StatusCode == http.StatusOK ||
		resp.StatusCode == http.StatusCreated) {
		return nil, handleError(b)
	}

	var result Response

	err = json.Unmarshal(b, &result)

	if err != nil {
		return nil, err
	}

	return &result, nil
}

func (c *Client) getHttpPath(random bool, s ...string) string {
	var machine string
	if random {
		machine = c.cluster.Machines[rand.Intn(len(c.cluster.Machines))]
	} else {
		machine = c.cluster.Leader
	}

	fullPath := machine + "/" + version
	for _, seg := range s {
		fullPath = fullPath + "/" + seg
	}

	return fullPath
}

// buildValues builds a url.Values map according to the given value and ttl
func buildValues(value string, ttl uint64) url.Values {
	v := url.Values{}

	if value != "" {
		v.Set("value", value)
	}

	if ttl > 0 {
		v.Set("ttl", fmt.Sprintf("%v", ttl))
	}

	return v
}
<commit_msg>refactor remove extra logic<commit_after>package etcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"
)

var (
	curlChan chan string
)

// SetCurlChan sets a channel to which cURL commands which can be used to
// re-produce requests are sent.  This is useful for debugging.
func SetCurlChan(c chan string) {
	curlChan = c
}

// get issues a GET request
func (c *Client) get(key string, options options) (*Response, error) {
	logger.Debugf("get %s [%s]", key, c.cluster.Leader)

	p := path.Join("keys", key)
	// If consistency level is set to STRONG, append
	// the `consistent` query string.
	if c.config.Consistency == STRONG_CONSISTENCY {
		options["consistent"] = true
	}

	str, err := options.toParameters(VALID_GET_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	resp, err := c.sendRequest("GET", p, nil)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// put issues a PUT request
func (c *Client) put(key string, value string, ttl uint64, options options) (*Response, error) {
	logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
	p := path.Join("keys", key)

	str, err := options.toParameters(VALID_PUT_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	resp, err := c.sendRequest("PUT", p, buildValues(value, ttl))

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// post issues a POST request
func (c *Client) post(key string, value string, ttl uint64) (*Response, error) {
	logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.Leader)
	p := path.Join("keys", key)

	resp, err := c.sendRequest("POST", p, buildValues(value, ttl))

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// delete issues a DELETE request
func (c *Client) delete(key string, options options) (*Response, error) {
	logger.Debugf("delete %s [%s]", key, c.cluster.Leader)

	p := path.Join("keys", key)

	str, err := options.toParameters(VALID_DELETE_OPTIONS)
	if err != nil {
		return nil, err
	}
	p += str

	resp, err := c.sendRequest("DELETE", p, nil)

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// sendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) sendRequest(method string, relativePath string, values url.Values) (*Response, error) {
	var resp *http.Response
	var req *http.Request

	trial := 0

	// if we connect to a follower, we will retry until we found a leader
	for {
		var httpPath string

		trial++
		if trial > 2*len(c.cluster.Machines) {
			return nil, fmt.Errorf("Cannot reach servers after %v time", trial)
		}

		if method == "GET" && c.config.Consistency == WEAK_CONSISTENCY {
			// If it's a GET and consistency level is set to WEAK,
			// then use a random machine.
			httpPath = c.getHttpPath(true, relativePath)
		} else {
			// Else use the leader.
			httpPath = c.getHttpPath(false, relativePath)
		}

		// Return a cURL command if curlChan is set
		if curlChan != nil {
			command := fmt.Sprintf("curl -X %s %s", method, httpPath)
			for key, value := range values {
				command += fmt.Sprintf(" -d %s=%s", key, value[0])
			}
			curlChan <- command
		}

		logger.Debug("send.request.to ", httpPath, " | method ", method)

		if values == nil {
			req, _ = http.NewRequest(method, httpPath, nil)
		} else {
			req, _ = http.NewRequest(method, httpPath,
				strings.NewReader(values.Encode()))

			req.Header.Set("Content-Type",
				"application/x-www-form-urlencoded; param=value")
		}

		resp, err := c.httpClient.Do(req)

		// network error, change a machine!
		if err != nil {
			c.switchLeader(trial % len(c.cluster.Machines))
			time.Sleep(time.Millisecond * 200)
			continue
		}

		if resp != nil {
			logger.Debug("recv.response.from ", httpPath)

			if resp.StatusCode == http.StatusTemporaryRedirect {
				httpPath := resp.Header.Get("Location")

				resp.Body.Close()

				if httpPath == "" {
					return nil, errors.New("Cannot get redirection location")
				}

				c.updateLeader(httpPath)
				logger.Debug("send.redirect")
				// try to connect the leader
				continue
			} else if resp.StatusCode == http.StatusInternalServerError {
				resp.Body.Close()
				time.Sleep(time.Millisecond * 200)
				continue
			} else {
				logger.Debug("send.return.response ", httpPath)
				break
			}

		}

		logger.Debug("error.from ", httpPath, " ", err.Error())
		return nil, err
	}

	// Convert HTTP response to etcd response
	b, err := ioutil.ReadAll(resp.Body)

	resp.Body.Close()

	if err != nil {
		return nil, err
	}

	if !(resp.StatusCode == http.StatusOK ||
		resp.StatusCode == http.StatusCreated) {
		return nil, handleError(b)
	}

	var result Response

	err = json.Unmarshal(b, &result)

	if err != nil {
		return nil, err
	}

	return &result, nil
}

func (c *Client) getHttpPath(random bool, s ...string) string {
	var machine string
	if random {
		machine = c.cluster.Machines[rand.Intn(len(c.cluster.Machines))]
	} else {
		machine = c.cluster.Leader
	}

	fullPath := machine + "/" + version
	for _, seg := range s {
		fullPath = fullPath + "/" + seg
	}

	return fullPath
}

// buildValues builds a url.Values map according to the given value and ttl
func buildValues(value string, ttl uint64) url.Values {
	v := url.Values{}

	if value != "" {
		v.Set("value", value)
	}

	if ttl > 0 {
		v.Set("ttl", fmt.Sprintf("%v", ttl))
	}

	return v
}
<|endoftext|>


<commit_before>package xsdgen

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

func glob(dir ...string) []string {
	files, err := filepath.Glob(filepath.Join(dir...))
	if err != nil {
		panic("error in glob util function: " + err.Error())
	}
	return files
}

type testLogger testing.T

func (t *testLogger) Printf(format string, v ...interface{}) {
	t.Logf(format, v...)
}

func TestLibrarySchema(t *testing.T) {
	testGen(t, "http://dyomedea.com/ns/library", "testdata/library.xsd")
}
func TestPurchasOrderSchema(t *testing.T) {
	testGen(t, "http://www.example.com/PO1", "testdata/po1.xsd")
}
func TestUSTreasureSDN(t *testing.T) {
	testGen(t, "http://tempuri.org/sdnList.xsd", "testdata/sdn.xsd")
}

func testGen(t *testing.T, ns string, files ...string) {
	file, err := ioutil.TempFile("", "xsdgen")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(file.Name())

	var cfg Config
	cfg.Option(DefaultOptions...)

	args := []string{"-o", file.Name(), "-ns", ns}
	err = cfg.GenCLI(append(args, files...)...)
	if err != nil {
		t.Error(err)
	}
	if data, err := ioutil.ReadFile(file.Name()); err != nil {
		t.Error(err)
	} else {
		t.Logf("\n%s\n", data)
	}
}
<commit_msg>Add logging to test runs<commit_after>package xsdgen

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

func glob(dir ...string) []string {
	files, err := filepath.Glob(filepath.Join(dir...))
	if err != nil {
		panic("error in glob util function: " + err.Error())
	}
	return files
}

type testLogger testing.T

func (t *testLogger) Printf(format string, v ...interface{}) {
	t.Logf(format, v...)
}

func TestLibrarySchema(t *testing.T) {
	testGen(t, "http://dyomedea.com/ns/library", "testdata/library.xsd")
}
func TestPurchasOrderSchema(t *testing.T) {
	testGen(t, "http://www.example.com/PO1", "testdata/po1.xsd")
}
func TestUSTreasureSDN(t *testing.T) {
	testGen(t, "http://tempuri.org/sdnList.xsd", "testdata/sdn.xsd")
}

func testGen(t *testing.T, ns string, files ...string) {
	file, err := ioutil.TempFile("", "xsdgen")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(file.Name())

	var cfg Config
	cfg.Option(DefaultOptions...)
	cfg.Option(LogOutput((*testLogger)(t)))

	args := []string{"-v", "-o", file.Name(), "-ns", ns}
	err = cfg.GenCLI(append(args, files...)...)
	if err != nil {
		t.Error(err)
	}
	if data, err := ioutil.ReadFile(file.Name()); err != nil {
		t.Error(err)
	} else {
		t.Logf("\n%s\n", data)
	}
}
<|endoftext|>


<commit_before>package junos

import (
	"encoding/xml"
	"fmt"
	"regexp"
)

// Devices contains a list of managed devices.
type Devices struct {
	XMLName xml.Name `xml:"devices"`
	Devices []Device `xml:"device"`
}

// A Device contains information about each individual device.
type Device struct {
	ID        int    `xml:"key,attr"`
	Family    string `xml:"deviceFamily"`
	Version   string `xml:"OSVersion"`
	Platform  string `xml:"platform"`
	Serial    string `xml:"serialNumber"`
	IPAddress string `xml:"ipAddr"`
	Name      string `xml:"name"`
}

// addDeviceIPXML is the XML we send (POST) for adding a device by IP address.
var addDeviceIPXML = `
<discover-devices>
    <ipAddressDiscoveryTarget>
        <ipAddress>%s</ipAddress>
    </ipAddressDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// addDeviceHostXML is the XML we send (POST) for adding a device by hostname.
var addDeviceHostXML = `
<discover-devices>
    <hostNameDiscoveryTarget>
        <hostName>%s</hostName>
    </hostNameDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// getDeviceID returns the ID of a managed device.
func (s *JunosSpace) getDeviceID(device interface{}) (int, error) {
	var err error
	var deviceID int
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
	devices, err := s.Devices()
	if err != nil {
		return 0, err
	}

	switch device.(type) {
	case int:
		deviceID = device.(int)
	case string:
		if ipRegex.MatchString(device.(string)) {
			for _, d := range devices.Devices {
				if d.IPAddress == device.(string) {
					deviceID = d.ID
				}
			}
		}
		for _, d := range devices.Devices {
			if d.Name == device.(string) {
				deviceID = d.ID
			}
		}
	}

	return deviceID, nil
}

// AddDevice adds a new managed device to Junos Space, and returns the Job ID.
func (s *JunosSpace) AddDevice(host, user, password string) (int, error) {
	var job jobID
	var addDevice string
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)

	if ipRegex.MatchString(host) {
		addDevice = addDeviceIPXML
	}

	addDevice = addDeviceHostXML

	req := &APIRequest{
		Method:      "post",
		URL:         "/api/space/device-management/discover-devices",
		Body:        fmt.Sprintf(addDevice, host, user, password),
		ContentType: contentDiscoverDevices,
	}
	data, err := s.APICall(req)
	if err != nil {
		return 0, err
	}

	err = xml.Unmarshal(data, &job)
	if err != nil {
		return 0, err
	}

	return job.ID, nil
}

// Devices queries the Junos Space server and returns all of the information
// about each device that is managed by Space.
func (s *JunosSpace) Devices() (*Devices, error) {
	var devices Devices
	req := &APIRequest{
		Method: "get",
		URL:    "/api/space/device-management/devices",
	}
	data, err := s.APICall(req)
	if err != nil {
		return nil, err
	}

	err = xml.Unmarshal(data, &devices)
	if err != nil {
		return nil, err
	}

	return &devices, nil
}

// RemoveDevice removes a device from Junos Space. You can specify the device ID, name
// or IP address.
func (s *JunosSpace) RemoveDevice(device interface{}) error {
	var err error
	deviceID, err := s.getDeviceID(device)
	if err != nil {
		return err
	}

	if deviceID != 0 {
		req := &APIRequest{
			Method: "delete",
			URL:    fmt.Sprintf("/api/space/device-management/devices/%d", deviceID),
		}
		_, err = s.APICall(req)
		if err != nil {
			return err
		}
	}

	return nil
}

// Resync synchronizes the device with Junos Space. Good to use if you make a lot of
// changes outside of Junos Space such as adding interfaces, zones, etc.
func (s *JunosSpace) Resync(device interface{}) (int, error) {
	var job jobID
	deviceID, err := s.getDeviceID(device)
	if err != nil {
		return 0, err
	}

	req := &APIRequest{
		Method:      "post",
		URL:         fmt.Sprintf("/api/space/device-management/devices/%d/exec-resync", deviceID),
		ContentType: contentResync,
	}
	data, err := s.APICall(req)
	if err != nil {
		return 0, err
	}

	err = xml.Unmarshal(data, &job)
	if err != nil {
		return 0, err
	}

	return job.ID, nil
}
<commit_msg>Added connection and managed status fields for returning device information<commit_after>package junos

import (
	"encoding/xml"
	"fmt"
	"regexp"
)

// Devices contains a list of managed devices.
type Devices struct {
	XMLName xml.Name `xml:"devices"`
	Devices []Device `xml:"device"`
}

// A Device contains information about each individual device.
type Device struct {
	ID               int    `xml:"key,attr"`
	Family           string `xml:"deviceFamily"`
	Version          string `xml:"OSVersion"`
	Platform         string `xml:"platform"`
	Serial           string `xml:"serialNumber"`
	IPAddress        string `xml:"ipAddr"`
	Name             string `xml:"name"`
	ConnectionStatus string `xml:"connectionStatus"`
	ManagedStatus    string `xml:"managedStatus"`
}

// addDeviceIPXML is the XML we send (POST) for adding a device by IP address.
var addDeviceIPXML = `
<discover-devices>
    <ipAddressDiscoveryTarget>
        <ipAddress>%s</ipAddress>
    </ipAddressDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// addDeviceHostXML is the XML we send (POST) for adding a device by hostname.
var addDeviceHostXML = `
<discover-devices>
    <hostNameDiscoveryTarget>
        <hostName>%s</hostName>
    </hostNameDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// getDeviceID returns the ID of a managed device.
func (s *JunosSpace) getDeviceID(device interface{}) (int, error) {
	var err error
	var deviceID int
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
	devices, err := s.Devices()
	if err != nil {
		return 0, err
	}

	switch device.(type) {
	case int:
		deviceID = device.(int)
	case string:
		if ipRegex.MatchString(device.(string)) {
			for _, d := range devices.Devices {
				if d.IPAddress == device.(string) {
					deviceID = d.ID
				}
			}
		}
		for _, d := range devices.Devices {
			if d.Name == device.(string) {
				deviceID = d.ID
			}
		}
	}

	return deviceID, nil
}

// AddDevice adds a new managed device to Junos Space, and returns the Job ID.
func (s *JunosSpace) AddDevice(host, user, password string) (int, error) {
	var job jobID
	var addDevice string
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)

	if ipRegex.MatchString(host) {
		addDevice = addDeviceIPXML
	}

	addDevice = addDeviceHostXML

	req := &APIRequest{
		Method:      "post",
		URL:         "/api/space/device-management/discover-devices",
		Body:        fmt.Sprintf(addDevice, host, user, password),
		ContentType: contentDiscoverDevices,
	}
	data, err := s.APICall(req)
	if err != nil {
		return 0, err
	}

	err = xml.Unmarshal(data, &job)
	if err != nil {
		return 0, err
	}

	return job.ID, nil
}

// Devices queries the Junos Space server and returns all of the information
// about each device that is managed by Space.
func (s *JunosSpace) Devices() (*Devices, error) {
	var devices Devices
	req := &APIRequest{
		Method: "get",
		URL:    "/api/space/device-management/devices",
	}
	data, err := s.APICall(req)
	if err != nil {
		return nil, err
	}

	err = xml.Unmarshal(data, &devices)
	if err != nil {
		return nil, err
	}

	return &devices, nil
}

// RemoveDevice removes a device from Junos Space. You can specify the device ID, name
// or IP address.
func (s *JunosSpace) RemoveDevice(device interface{}) error {
	var err error
	deviceID, err := s.getDeviceID(device)
	if err != nil {
		return err
	}

	if deviceID != 0 {
		req := &APIRequest{
			Method: "delete",
			URL:    fmt.Sprintf("/api/space/device-management/devices/%d", deviceID),
		}
		_, err = s.APICall(req)
		if err != nil {
			return err
		}
	}

	return nil
}

// Resync synchronizes the device with Junos Space. Good to use if you make a lot of
// changes outside of Junos Space such as adding interfaces, zones, etc.
func (s *JunosSpace) Resync(device interface{}) (int, error) {
	var job jobID
	deviceID, err := s.getDeviceID(device)
	if err != nil {
		return 0, err
	}

	req := &APIRequest{
		Method:      "post",
		URL:         fmt.Sprintf("/api/space/device-management/devices/%d/exec-resync", deviceID),
		ContentType: contentResync,
	}
	data, err := s.APICall(req)
	if err != nil {
		return 0, err
	}

	err = xml.Unmarshal(data, &job)
	if err != nil {
		return 0, err
	}

	return job.ID, nil
}
<|endoftext|>


<commit_before>/*******************************************************************************
 * This is very experimental code and probably a long way from perfect or
 * ideal.  Please provide feed back on areas that would improve performance
 *
 */
package dgvoice

import (
	"encoding/binary"
	"fmt"
	"io"
	"os/exec"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/layeh/gopus"
)

// PlayAudioFile will play the given filename to the already connected
// Discord voice server/channel.  voice websocket and udp socket
// must already be setup before this will work.
func PlayAudioFile(s *discordgo.Session, filename string) {

	var sequence uint16 = 0  // used for voice play test
	var timestamp uint32 = 0 // used for voice play test

	opusEncoder, err := gopus.NewEncoder(48000, 1, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}
	opusEncoder.SetBitrate(96000)

	// Create a shell command "object" to run.
	run := exec.Command("ffmpeg", "-i", filename, "-f", "s16le", "-ar", "48000", "-ac", "1", "pipe:1")
	stdout, err := run.StdoutPipe()
	if err != nil {
		fmt.Println("StdoutPipe Error:", err)
		return
	}

	// Starts the ffmpeg command
	err = run.Start()
	if err != nil {
		fmt.Println("RunStart Error:", err)
		return
	}

	// variables used during loop below
	udpPacket := make([]byte, 4000)
	audiobuf := make([]int16, 960)

	// build the parts that don't change in the udpPacket.
	udpPacket[0] = 0x80
	udpPacket[1] = 0x78
	binary.BigEndian.PutUint32(udpPacket[8:], s.Vop2.SSRC)

	// Send "speaking" packet over the voice websocket
	s.VoiceSpeaking()

	// start a 20ms read/encode/send loop that loops until EOF from ffmpeg
	ticker := time.NewTicker(time.Millisecond * 20)
	for {
		// Add sequence and timestamp to udpPacket
		binary.BigEndian.PutUint16(udpPacket[2:], sequence)
		binary.BigEndian.PutUint32(udpPacket[4:], timestamp)

		// read 1920 bytes (960 int16) from ffmpeg stdout
		err = binary.Read(stdout, binary.LittleEndian, &audiobuf)
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			fmt.Println("Reached EOF.")
			return
		}
		if err != nil {
			fmt.Println("Playback Error:", err)
			return
		}

		// try encoding ffmpeg frame with Opus
		opus, err := opusEncoder.Encode(audiobuf, 960, 1920)
		if err != nil {
			fmt.Println("Encoding Error:", err)
			return
		}

		// copy opus result into udpPacket
		copy(udpPacket[12:], opus)

		<-ticker.C // block here until we're exactly at 20ms
		s.UDPConn.Write(udpPacket[:(len(opus) + 12)])

		// increment sequence and timestamp
		// timestamp should be calculated based on something.. :)
		if (sequence) == 0xFFFF {
			sequence = 0
		} else {
			sequence += 1 // this just increments each loop
		}

		if (timestamp + 960) >= 0xFFFFFFFF {
			timestamp = 0
		} else {
			timestamp += 960 // also just increments each loop
		}

	}
}
<commit_msg>Added variables for frame sizes and other settings<commit_after>/*******************************************************************************
 * This is very experimental code and probably a long way from perfect or
 * ideal.  Please provide feed back on areas that would improve performance
 *
 */
package dgvoice

import (
	"encoding/binary"
	"fmt"
	"io"
	"os/exec"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/layeh/gopus"
)

// PlayAudioFile will play the given filename to the already connected
// Discord voice server/channel.  voice websocket and udp socket
// must already be setup before this will work.

// Settings.
var (
	FrameRate   int = 48000                            // sample rate of frames
	FrameTime   int = 60                               // Length of audio frame in ms (20, 40, 60)
	FrameLength int = ((FrameRate / 1000) * FrameTime) // Length of frame as uint16 array
	OpusBitrate int = 96000                            // Bitrate to use when encoding
	OpusMaxSize int = (FrameLength * 2)                // max size opus encoder can return
)

func PlayAudioFile(s *discordgo.Session, filename string) {

	var sequence uint16 = 0  // used for voice play test
	var timestamp uint32 = 0 // used for voice play test

	opusEncoder, err := gopus.NewEncoder(FrameRate, 1, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}
	opusEncoder.SetBitrate(OpusBitrate)

	// Create a shell command "object" to run.
	run := exec.Command("ffmpeg", "-i", filename, "-f", "s16le", "-ar", "48000", "-ac", "1", "pipe:1")
	stdout, err := run.StdoutPipe()
	if err != nil {
		fmt.Println("StdoutPipe Error:", err)
		return
	}

	// Starts the ffmpeg command
	err = run.Start()
	if err != nil {
		fmt.Println("RunStart Error:", err)
		return
	}

	// variables used during loop below
	udpPacket := make([]byte, OpusMaxSize)
	audiobuf := make([]int16, FrameLength)

	// build the parts that don't change in the udpPacket.
	udpPacket[0] = 0x80
	udpPacket[1] = 0x78
	binary.BigEndian.PutUint32(udpPacket[8:], s.Vop2.SSRC)

	// Send "speaking" packet over the voice websocket
	s.VoiceSpeaking()

	// start a 20ms read/encode/send loop that loops until EOF from ffmpeg
	ticker := time.NewTicker(time.Millisecond * time.Duration(FrameTime))
	for {
		// Add sequence and timestamp to udpPacket
		binary.BigEndian.PutUint16(udpPacket[2:], sequence)
		binary.BigEndian.PutUint32(udpPacket[4:], timestamp)

		// read 1920 bytes (960 int16) from ffmpeg stdout
		err = binary.Read(stdout, binary.LittleEndian, &audiobuf)
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			fmt.Println("Reached EOF.")
			return
		}
		if err != nil {
			fmt.Println("Playback Error:", err)
			return
		}

		// try encoding ffmpeg frame with Opus
		opus, err := opusEncoder.Encode(audiobuf, FrameLength, OpusMaxSize)
		if err != nil {
			fmt.Println("Encoding Error:", err)
			return
		}

		// copy opus result into udpPacket
		copy(udpPacket[12:], opus)

		// block here until we're exactly at 20ms
		<-ticker.C

		// Send rtp audio packet to Discord over UDP
		s.UDPConn.Write(udpPacket[:(len(opus) + 12)])

		if (sequence) == 0xFFFF {
			sequence = 0
		} else {
			sequence += 1
		}

		if (timestamp + uint32(FrameLength)) >= 0xFFFFFFFF {
			timestamp = 0
		} else {
			timestamp += uint32(FrameLength)
		}
	}
}
<|endoftext|>


<commit_before>package sqldb

import (
	"fmt"
	"strings"
)

type Postgres struct{}

func (Postgres) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "\\", "\\\\'", -1)
			val = strings.Replace(val, "'", "\\'", -1)
			val = `E'` + val + `'`
		} else {
			val = `'` + val + `'`
		}
	}
	return val
}

func (p Postgres) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool":
		return "BOOLEAN", p.defaultVal("false", val, false), nil
	case "int":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int16":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int32":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "uint16":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "uint32":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "float32", "float64", "float":
		if precision != "" {
			typ = fmt.Sprintf("NUMERIC(%s)", precision)
		} else if typ == "float32" {
			typ = "REAL"
		} else {
			typ = "DOUBLE PRECISION"
		}
		return typ, p.defaultVal("0", val, false), nil
	case "string":
		if precision == "" {
			precision = "64"
		}
		return fmt.Sprintf("VARCHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "char":
		if precision == "" {
			precision = "64"
		}
		return fmt.Sprintf("CHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "text":
		return "TEXT", p.defaultVal("", val, true), nil
	case "blob":
		return "BYTEA", p.defaultVal("E'\\000'", val, false), nil
	default:
		return "", "", fmt.Errorf("postgres: unsupported type: %s", typ)
	}
}

func (Postgres) DSN(config DBConfig) string {
	if config.Host == "" {
		config.Host = "localhost"
	}
	if config.Port == 0 {
		config.Port = 5432
	}
	userPass := config.User
	if userPass != "" {
		if config.Password != "" {
			userPass += ":" + config.Password
		}
		userPass += "@"
	}
	return fmt.Sprintf(
		"postgres://%s%s:%d/%s?%s",
		userPass,
		config.Host,
		config.Port,
		config.DBName,
		config.JoinOptions("=", "&"),
	)
}

type SQLite3 struct{}

func (SQLite3) DSN(config DBConfig) string {
	if config.Host == "" || config.DBName == "" {
		return ":memory:"
	}
	return fmt.Sprintf("file:%s?%s", config.DBName, config.JoinOptions("=", "&"))
}

func (s SQLite3) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "'", "''", -1)
		}
		val = `'` + val + `'`
	}
	return val
}

func (s SQLite3) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool",
		"int",
		"int8",
		"int16",
		"int32",
		"int64",
		"uint",
		"uint8",
		"uint16",
		"uint32",
		"uint64":
		return "INTEGER", s.defaultVal("0", val, false), nil
	case "float32",
		"float64",
		"float":
		return "FLOAT", s.defaultVal("0", val, false), nil
	case "string",
		"text",
		"char":
		return "TEXT", s.defaultVal("", val, true), nil
	case "blob":
		return "BLOB", s.defaultVal("x''", val, false), nil
	default:
		return "", "", fmt.Errorf("sqlite3: unsupported type: %s", typ)
	}
}
<commit_msg>fixed default value for postgresql bytea type.<commit_after>package sqldb

import (
	"fmt"
	"strings"
)

type Postgres struct{}

func (Postgres) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "\\", "\\\\'", -1)
			val = strings.Replace(val, "'", "\\'", -1)
			val = `E'` + val + `'`
		} else {
			val = `'` + val + `'`
		}
	}
	return val
}

func (p Postgres) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool":
		return "BOOLEAN", p.defaultVal("false", val, false), nil
	case "int":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int16":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "int32":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "int64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint8":
		return "SMALLINT", p.defaultVal("0", val, false), nil
	case "uint16":
		return "INTEGER", p.defaultVal("0", val, false), nil
	case "uint32":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "uint64":
		return "BIGINT", p.defaultVal("0", val, false), nil
	case "float32", "float64", "float":
		if precision != "" {
			typ = fmt.Sprintf("NUMERIC(%s)", precision)
		} else if typ == "float32" {
			typ = "REAL"
		} else {
			typ = "DOUBLE PRECISION"
		}
		return typ, p.defaultVal("0", val, false), nil
	case "string":
		if precision == "" {
			precision = "64"
		}
		return fmt.Sprintf("VARCHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "char":
		if precision == "" {
			precision = "64"
		}
		return fmt.Sprintf("CHAR(%s)", precision), p.defaultVal("", val, true), nil
	case "text":
		return "TEXT", p.defaultVal("", val, true), nil
	case "blob":
		return "BYTEA", p.defaultVal(`E'\\000'`, val, false), nil
	default:
		return "", "", fmt.Errorf("postgres: unsupported type: %s", typ)
	}
}

func (Postgres) DSN(config DBConfig) string {
	if config.Host == "" {
		config.Host = "localhost"
	}
	if config.Port == 0 {
		config.Port = 5432
	}
	userPass := config.User
	if userPass != "" {
		if config.Password != "" {
			userPass += ":" + config.Password
		}
		userPass += "@"
	}
	return fmt.Sprintf(
		"postgres://%s%s:%d/%s?%s",
		userPass,
		config.Host,
		config.Port,
		config.DBName,
		config.JoinOptions("=", "&"),
	)
}

type SQLite3 struct{}

func (SQLite3) DSN(config DBConfig) string {
	if config.Host == "" || config.DBName == "" {
		return ":memory:"
	}
	return fmt.Sprintf("file:%s?%s", config.DBName, config.JoinOptions("=", "&"))
}

func (s SQLite3) defaultVal(def, val string, quote bool) string {
	if val == "" {
		val = def
	}
	if quote {
		if strings.Contains(val, "'") {
			val = strings.Replace(val, "'", "''", -1)
		}
		val = `'` + val + `'`
	}
	return val
}

func (s SQLite3) Type(typ, precision, val string) (dbtyp, defval string, err error) {
	switch typ {
	case "bool",
		"int",
		"int8",
		"int16",
		"int32",
		"int64",
		"uint",
		"uint8",
		"uint16",
		"uint32",
		"uint64":
		return "INTEGER", s.defaultVal("0", val, false), nil
	case "float32",
		"float64",
		"float":
		return "FLOAT", s.defaultVal("0", val, false), nil
	case "string",
		"text",
		"char":
		return "TEXT", s.defaultVal("", val, true), nil
	case "blob":
		return "BLOB", s.defaultVal("x''", val, false), nil
	default:
		return "", "", fmt.Errorf("sqlite3: unsupported type: %s", typ)
	}
}
<|endoftext|>


<commit_before>package response

import (
	"github.com/grafana/metrictank/api/models"
	"gopkg.in/raintank/schema.v1"
)

type series struct {
	in  []models.Series
	out string
}

func testSeries() []series {
	cases := []series{
		{
			in:  []models.Series{},
			out: `[]`,
		},
		{
			in: []models.Series{
				{
					Target:     "a",
					Datapoints: []schema.Point{},
					Interval:   60,
				},
			},
			out: `[{"target":"a","datapoints":[]}]`,
		},
		{
			in: []models.Series{
				{
					Target: "a",
					Datapoints: []schema.Point{
						{123, 60},
						{10000, 120},
						{0, 180},
						{1, 240},
					},
					Interval: 60,
				},
			},
			out: `[{"target":"a","datapoints":[[123.000,60],[10000.000,120],[0.000,180],[1.000,240]]}]`,
		},
		{
			in: []models.Series{
				{
					Target: "a",
					Datapoints: []schema.Point{
						{123, 60},
						{10000, 120},
						{0, 180},
						{1, 240},
					},
					Interval: 60,
				},
				{
					Target: "foo(bar)",
					Datapoints: []schema.Point{
						{123.456, 10},
						{123.7, 20},
						{124.10, 30},
						{125.0, 40},
						{126.0, 50},
					},
					Interval: 10,
				},
			},
			out: `[{"target":"a","datapoints":[[123.000,60],[10000.000,120],[0.000,180],[1.000,240]]},{"target":"foo(bar)","datapoints":[[123.456,10],[123.700,20],[124.100,30],[125.000,40],[126.000,50]]}]`,
		},
	}
	return cases
}
<commit_msg>Fix test case<commit_after>package response

import (
	"github.com/grafana/metrictank/api/models"
	"gopkg.in/raintank/schema.v1"
)

type series struct {
	in  []models.Series
	out string
}

func testSeries() []series {
	cases := []series{
		{
			in:  []models.Series{},
			out: `[]`,
		},
		{
			in: []models.Series{
				{
					Target:     "a",
					Datapoints: []schema.Point{},
					Interval:   60,
				},
			},
			out: `[{"target":"a","datapoints":[]}]`,
		},
		{
			in: []models.Series{
				{
					Target: "a",
					Datapoints: []schema.Point{
						{123, 60},
						{10000, 120},
						{0, 180},
						{1, 240},
					},
					Interval: 60,
				},
			},
			out: `[{"target":"a","datapoints":[[123,60],[10000,120],[0,180],[1,240]]}]`,
		},
		{
			in: []models.Series{
				{
					Target: "a",
					Datapoints: []schema.Point{
						{123, 60},
						{10000, 120},
						{0, 180},
						{1, 240},
					},
					Interval: 60,
				},
				{
					Target: "foo(bar)",
					Datapoints: []schema.Point{
						{123.456, 10},
						{123.7, 20},
						{124.10, 30},
						{125.0, 40},
						{126.0, 50},
					},
					Interval: 10,
				},
			},
			out: `[{"target":"a","datapoints":[[123,60],[10000,120],[0,180],[1,240]]},{"target":"foo(bar)","datapoints":[[123.456,10],[123.7,20],[124.1,30],[125,40],[126,50]]}]`,
		},
	}
	return cases
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"strconv"
	"text/tabwriter"

	"github.com/coreos/updateservicectl/client/update/v1"
)

var (
	groupFlags struct {
		label          StringFlag
		channel        StringFlag
		appId          StringFlag
		groupId        StringFlag
		start          int64
		end            int64
		resolution     int64
		updateCount    int64
		updateInterval int64
	}

	cmdGroup = &Command{
		Name:    "group",
		Summary: "Operations that manage groups in an application.",
		Subcommands: []*Command{
			cmdGroupList,
			cmdGroupCreate,
			cmdGroupDelete,
			cmdGroupUpdate,
			cmdGroupPause,
			cmdGroupUnpause,
			cmdGroupEvents,
			cmdGroupVersions,
		},
	}

	cmdGroupList = &Command{
		Name:    "group list",
		Usage:   "[OPTION]...",
		Summary: `List all of the groups that exist including their label, token and update state.`,
		Run:     groupList,
	}
	cmdGroupCreate = &Command{
		Name:    "group create",
		Usage:   "[OPTION]...",
		Summary: `Create a new group.`,
		Run:     groupCreate,
	}
	cmdGroupDelete = &Command{
		Name:    "group delete",
		Usage:   "[OPTION]...",
		Summary: `Delete a group.`,
		Run:     groupDelete,
	}
	cmdGroupUpdate = &Command{
		Name:        "group update",
		Usage:       "[OPTION]...",
		Description: `Update an existing group.`,
		Run:         groupUpdate,
	}
	cmdGroupPause = &Command{
		Name:    "group pause",
		Usage:   "[OPTION]...",
		Summary: `Pause a group's updates.`,
		Run:     groupPause,
	}
	cmdGroupUnpause = &Command{
		Name:    "group unpause",
		Usage:   "[OPTION]...",
		Summary: `Unpause a group's updates.`,
		Run:     groupUnpause,
	}
	cmdGroupVersions = &Command{
		Name:    "group versions",
		Usage:   "[OPTION]...",
		Summary: "List versions from clients by time.",
		Run:     groupVersions,
	}
	cmdGroupEvents = &Command{
		Name:    "group events",
		Usage:   "[OPTION]...",
		Summary: "List events from clients by time.",
		Run:     groupEvents,
	}
)

func init() {
	cmdGroupList.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the groups to list.")

	cmdGroupDelete.Flags.Var(&groupFlags.appId, "app-id",
		"Application with group to delete.")
	cmdGroupDelete.Flags.Var(&groupFlags.groupId, "group-id",
		"ID of group to delete.")

	cmdGroupCreate.Flags.Var(&groupFlags.appId, "app-id",
		"Application to add group to.")
	cmdGroupCreate.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the new group.")
	cmdGroupCreate.Flags.Var(&groupFlags.channel, "channel",
		"Channel to associate with the group.")
	cmdGroupCreate.Flags.Var(&groupFlags.label, "label",
		"Label describing the new group.")

	cmdGroupUpdate.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group to update.")
	cmdGroupUpdate.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")
	cmdGroupUpdate.Flags.Var(&groupFlags.label, "label",
		"Label describing the group")
	cmdGroupUpdate.Flags.Var(&groupFlags.channel, "channel",
		"Channel to associate with the group.")
	cmdGroupUpdate.Flags.Int64Var(&groupFlags.updateCount, "update-count",
		-1, "Number of instances per interval")
	cmdGroupUpdate.Flags.Int64Var(&groupFlags.updateInterval,
		"update-interval", -1, "Interval between updates")

	cmdGroupPause.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group to pause.")
	cmdGroupPause.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")

	cmdGroupUnpause.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group to unpause.")
	cmdGroupUnpause.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")

	cmdGroupVersions.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group.")
	cmdGroupVersions.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")
	cmdGroupVersions.Flags.Int64Var(&groupFlags.resolution,
		"resolution", 60, "60, 3600 or 86400 seconds")
	cmdGroupVersions.Flags.Int64Var(&groupFlags.start, "start", 0,
		"Start date filter")
	cmdGroupVersions.Flags.Int64Var(&groupFlags.end, "end", 0,
		"End date filter")

	cmdGroupEvents.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group.")
	cmdGroupEvents.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")
	cmdGroupEvents.Flags.Int64Var(&groupFlags.resolution,
		"resolution", 60, "60, 3600 or 86400 seconds")
	cmdGroupEvents.Flags.Int64Var(&groupFlags.start, "start", 0,
		"Start date filter")
	cmdGroupEvents.Flags.Int64Var(&groupFlags.end, "end", 0,
		"End date filter")
}

const groupHeader = "Label\tApp\tChannel\tId\tPaused\tCount\tInterval\n"

func formatGroup(group *update.Group) string {
	return fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%v\t%v\n",
		group.Label, group.AppId, group.ChannelId,
		group.Id, strconv.FormatBool(group.UpdatesPaused),
		group.UpdateCount, group.UpdateInterval)
}

func groupList(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil {
		return ERROR_USAGE
	}

	listCall := service.Group.List(groupFlags.appId.String())
	list, err := listCall.Do()

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

	fmt.Fprint(out, groupHeader)
	for _, group := range list.Items {
		fmt.Fprintf(out, "%s", formatGroup(group))
	}

	out.Flush()
	return OK
}

func groupEvents(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil || groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Requests.Events.Rollup(
		groupFlags.appId.String(),
		groupFlags.groupId.String(),
		groupFlags.start,
		groupFlags.end,
	)
	call.Resolution(groupFlags.resolution)
	list, err := call.Do()

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

	fmt.Fprintln(out, "Version\tType\tResult\tTimestamp\tCount")
	for _, i := range list.Items {
		for _, j := range i.Values {
			fmt.Fprintf(out, "%s\t%s\t%s\t%d\t%d\n",
				i.Version, i.Type, i.Result, j.Timestamp, j.Count)
		}
	}
	out.Flush()
	return OK
}

func groupVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil || groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Requests.Versions.Rollup(
		groupFlags.appId.String(),
		groupFlags.groupId.String(),
		groupFlags.start,
		groupFlags.end,
	)
	call.Resolution(groupFlags.resolution)
	list, err := call.Do()

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

	fmt.Fprintln(out, "Version\tTimestamp\tCount")
	for _, i := range list.Items {
		for _, j := range i.Values {
			fmt.Fprintf(out, "%s\t%d\t%d\n",
				i.Version, j.Timestamp, j.Count)
		}
	}
	out.Flush()
	return OK
}

func groupCreate(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil ||
		groupFlags.channel.Get() == nil {
		return ERROR_USAGE
	}

	group := &update.Group{
		ChannelId: groupFlags.channel.String(),
		Id:        groupFlags.groupId.String(),
		Label:     groupFlags.label.String(),
	}
	call := service.Group.Insert(groupFlags.appId.String(), group)
	group, err := call.Do()

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

	fmt.Fprint(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))
	out.Flush()
	return OK
}

func groupDelete(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Delete(groupFlags.appId.String(), groupFlags.groupId.String())
	group, err := call.Do()

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

	fmt.Fprint(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))
	out.Flush()
	return OK
}

func groupPause(args []string, service *update.Service, out *tabwriter.Writer) int {
	return setUpdatesPaused(service, out, true)
}

func groupUnpause(args []string, service *update.Service, out *tabwriter.Writer) int {
	return setUpdatesPaused(service, out, false)
}

// Helper function for pause/unpause-group commands
func setUpdatesPaused(service *update.Service, out *tabwriter.Writer, paused bool) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Get(groupFlags.appId.String(), groupFlags.groupId.String())
	group, err := call.Do()

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

	group.UpdatesPaused = paused

	updateCall := service.Group.Patch(groupFlags.appId.String(), groupFlags.groupId.String(), group)
	group, err = updateCall.Do()

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

	fmt.Fprint(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))

	out.Flush()
	return OK
}

func groupUpdate(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Get(groupFlags.appId.String(), groupFlags.groupId.String())
	group, err := call.Do()

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

	checkUpdatePooling := false
	if groupFlags.updateCount != -1 {
		group.UpdateCount = groupFlags.updateCount
		checkUpdatePooling = true
	}
	if groupFlags.updateInterval != -1 {
		group.UpdateInterval = groupFlags.updateInterval
		checkUpdatePooling = true
	}
	if groupFlags.label.Get() != nil {
		group.Label = groupFlags.label.String()
	}
	if groupFlags.channel.Get() != nil {
		group.ChannelId = groupFlags.channel.String()
	}

	// set update pooling based on other flags
	// this only changes if the user changed a value
	if checkUpdatePooling {
		if group.UpdateCount == 0 && group.UpdateInterval == 0 {
			group.UpdatePooling = false
		} else {
			group.UpdatePooling = true
		}
	}

	updateCall := service.Group.Patch(groupFlags.appId.String(), groupFlags.groupId.String(), group)
	group, err = updateCall.Do()

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

	fmt.Fprintln(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))

	out.Flush()
	return OK
}
<commit_msg>group: Add an option to update OemBlacklist<commit_after>package main

import (
	"fmt"
	"log"
	"strconv"
	"text/tabwriter"

	"github.com/coreos/updateservicectl/client/update/v1"
)

var (
	groupFlags struct {
		label          StringFlag
		channel        StringFlag
		appId          StringFlag
		groupId        StringFlag
		oemBlacklist   StringFlag
		start          int64
		end            int64
		resolution     int64
		updateCount    int64
		updateInterval int64
	}

	cmdGroup = &Command{
		Name:    "group",
		Summary: "Operations that manage groups in an application.",
		Subcommands: []*Command{
			cmdGroupList,
			cmdGroupCreate,
			cmdGroupDelete,
			cmdGroupUpdate,
			cmdGroupPause,
			cmdGroupUnpause,
			cmdGroupEvents,
			cmdGroupVersions,
		},
	}

	cmdGroupList = &Command{
		Name:    "group list",
		Usage:   "[OPTION]...",
		Summary: `List all of the groups that exist including their label, token and update state.`,
		Run:     groupList,
	}
	cmdGroupCreate = &Command{
		Name:    "group create",
		Usage:   "[OPTION]...",
		Summary: `Create a new group.`,
		Run:     groupCreate,
	}
	cmdGroupDelete = &Command{
		Name:    "group delete",
		Usage:   "[OPTION]...",
		Summary: `Delete a group.`,
		Run:     groupDelete,
	}
	cmdGroupUpdate = &Command{
		Name:        "group update",
		Usage:       "[OPTION]...",
		Description: `Update an existing group.`,
		Run:         groupUpdate,
	}
	cmdGroupPause = &Command{
		Name:    "group pause",
		Usage:   "[OPTION]...",
		Summary: `Pause a group's updates.`,
		Run:     groupPause,
	}
	cmdGroupUnpause = &Command{
		Name:    "group unpause",
		Usage:   "[OPTION]...",
		Summary: `Unpause a group's updates.`,
		Run:     groupUnpause,
	}
	cmdGroupVersions = &Command{
		Name:    "group versions",
		Usage:   "[OPTION]...",
		Summary: "List versions from clients by time.",
		Run:     groupVersions,
	}
	cmdGroupEvents = &Command{
		Name:    "group events",
		Usage:   "[OPTION]...",
		Summary: "List events from clients by time.",
		Run:     groupEvents,
	}
)

func init() {
	cmdGroupList.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the groups to list.")

	cmdGroupDelete.Flags.Var(&groupFlags.appId, "app-id",
		"Application with group to delete.")
	cmdGroupDelete.Flags.Var(&groupFlags.groupId, "group-id",
		"ID of group to delete.")

	cmdGroupCreate.Flags.Var(&groupFlags.appId, "app-id",
		"Application to add group to.")
	cmdGroupCreate.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the new group.")
	cmdGroupCreate.Flags.Var(&groupFlags.channel, "channel",
		"Channel to associate with the group.")
	cmdGroupCreate.Flags.Var(&groupFlags.label, "label",
		"Label describing the new group.")

	cmdGroupUpdate.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group to update.")
	cmdGroupUpdate.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")
	cmdGroupUpdate.Flags.Var(&groupFlags.label, "label",
		"Label describing the group")
	cmdGroupUpdate.Flags.Var(&groupFlags.channel, "channel",
		"Channel to associate with the group.")
	cmdGroupUpdate.Flags.Var(&groupFlags.oemBlacklist, "oem-blacklist",
		"Comma-separated list of OEMs to exclude from updates.")
	cmdGroupUpdate.Flags.Int64Var(&groupFlags.updateCount, "update-count",
		-1, "Number of instances per interval")
	cmdGroupUpdate.Flags.Int64Var(&groupFlags.updateInterval,
		"update-interval", -1, "Interval between updates")

	cmdGroupPause.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group to pause.")
	cmdGroupPause.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")

	cmdGroupUnpause.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group to unpause.")
	cmdGroupUnpause.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")

	cmdGroupVersions.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group.")
	cmdGroupVersions.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")
	cmdGroupVersions.Flags.Int64Var(&groupFlags.resolution,
		"resolution", 60, "60, 3600 or 86400 seconds")
	cmdGroupVersions.Flags.Int64Var(&groupFlags.start, "start", 0,
		"Start date filter")
	cmdGroupVersions.Flags.Int64Var(&groupFlags.end, "end", 0,
		"End date filter")

	cmdGroupEvents.Flags.Var(&groupFlags.appId, "app-id",
		"Application containing the group.")
	cmdGroupEvents.Flags.Var(&groupFlags.groupId, "group-id",
		"ID for the group.")
	cmdGroupEvents.Flags.Int64Var(&groupFlags.resolution,
		"resolution", 60, "60, 3600 or 86400 seconds")
	cmdGroupEvents.Flags.Int64Var(&groupFlags.start, "start", 0,
		"Start date filter")
	cmdGroupEvents.Flags.Int64Var(&groupFlags.end, "end", 0,
		"End date filter")
}

const groupHeader = "Label\tApp\tChannel\tId\tPaused\tCount\tInterval\n"

func formatGroup(group *update.Group) string {
	return fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%v\t%v\n",
		group.Label, group.AppId, group.ChannelId,
		group.Id, strconv.FormatBool(group.UpdatesPaused),
		group.UpdateCount, group.UpdateInterval)
}

func groupList(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil {
		return ERROR_USAGE
	}

	listCall := service.Group.List(groupFlags.appId.String())
	list, err := listCall.Do()

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

	fmt.Fprint(out, groupHeader)
	for _, group := range list.Items {
		fmt.Fprintf(out, "%s", formatGroup(group))
	}

	out.Flush()
	return OK
}

func groupEvents(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil || groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Requests.Events.Rollup(
		groupFlags.appId.String(),
		groupFlags.groupId.String(),
		groupFlags.start,
		groupFlags.end,
	)
	call.Resolution(groupFlags.resolution)
	list, err := call.Do()

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

	fmt.Fprintln(out, "Version\tType\tResult\tTimestamp\tCount")
	for _, i := range list.Items {
		for _, j := range i.Values {
			fmt.Fprintf(out, "%s\t%s\t%s\t%d\t%d\n",
				i.Version, i.Type, i.Result, j.Timestamp, j.Count)
		}
	}
	out.Flush()
	return OK
}

func groupVersions(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil || groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Requests.Versions.Rollup(
		groupFlags.appId.String(),
		groupFlags.groupId.String(),
		groupFlags.start,
		groupFlags.end,
	)
	call.Resolution(groupFlags.resolution)
	list, err := call.Do()

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

	fmt.Fprintln(out, "Version\tTimestamp\tCount")
	for _, i := range list.Items {
		for _, j := range i.Values {
			fmt.Fprintf(out, "%s\t%d\t%d\n",
				i.Version, j.Timestamp, j.Count)
		}
	}
	out.Flush()
	return OK
}

func groupCreate(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil ||
		groupFlags.channel.Get() == nil {
		return ERROR_USAGE
	}

	group := &update.Group{
		ChannelId: groupFlags.channel.String(),
		Id:        groupFlags.groupId.String(),
		Label:     groupFlags.label.String(),
	}
	call := service.Group.Insert(groupFlags.appId.String(), group)
	group, err := call.Do()

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

	fmt.Fprint(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))
	out.Flush()
	return OK
}

func groupDelete(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Delete(groupFlags.appId.String(), groupFlags.groupId.String())
	group, err := call.Do()

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

	fmt.Fprint(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))
	out.Flush()
	return OK
}

func groupPause(args []string, service *update.Service, out *tabwriter.Writer) int {
	return setUpdatesPaused(service, out, true)
}

func groupUnpause(args []string, service *update.Service, out *tabwriter.Writer) int {
	return setUpdatesPaused(service, out, false)
}

// Helper function for pause/unpause-group commands
func setUpdatesPaused(service *update.Service, out *tabwriter.Writer, paused bool) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Get(groupFlags.appId.String(), groupFlags.groupId.String())
	group, err := call.Do()

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

	group.UpdatesPaused = paused

	updateCall := service.Group.Patch(groupFlags.appId.String(), groupFlags.groupId.String(), group)
	group, err = updateCall.Do()

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

	fmt.Fprint(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))

	out.Flush()
	return OK
}

func groupUpdate(args []string, service *update.Service, out *tabwriter.Writer) int {
	if groupFlags.appId.Get() == nil ||
		groupFlags.groupId.Get() == nil {
		return ERROR_USAGE
	}

	call := service.Group.Get(groupFlags.appId.String(), groupFlags.groupId.String())
	group, err := call.Do()

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

	checkUpdatePooling := false
	if groupFlags.updateCount != -1 {
		group.UpdateCount = groupFlags.updateCount
		checkUpdatePooling = true
	}
	if groupFlags.updateInterval != -1 {
		group.UpdateInterval = groupFlags.updateInterval
		checkUpdatePooling = true
	}
	if groupFlags.label.Get() != nil {
		group.Label = groupFlags.label.String()
	}
	if groupFlags.channel.Get() != nil {
		group.ChannelId = groupFlags.channel.String()
	}
	if groupFlags.oemBlacklist.Get() != nil {
		group.OemBlacklist = groupFlags.oemBlacklist.String()
	}

	// set update pooling based on other flags
	// this only changes if the user changed a value
	if checkUpdatePooling {
		if group.UpdateCount == 0 && group.UpdateInterval == 0 {
			group.UpdatePooling = false
		} else {
			group.UpdatePooling = true
		}
	}

	updateCall := service.Group.Patch(groupFlags.appId.String(), groupFlags.groupId.String(), group)
	group, err = updateCall.Do()

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

	fmt.Fprintln(out, groupHeader)
	fmt.Fprintf(out, "%s", formatGroup(group))

	out.Flush()
	return OK
}
<|endoftext|>


<commit_before>package air

import "path"

// Group is a set of sub-routes for a specified route. It can be used for inner
// routes that share a common gas or functionality that should be separate
// from the parent `Air` instance while still inheriting from it.
type Group struct {
	prefix string
	gases  []GasFunc
	air    *Air
}

// NewGroup returns a new router group with prefix and optional group-level gases.
func NewGroup(prefix string, a *Air, gases ...GasFunc) *Group {
	g := &Group{prefix: prefix, air: a}
	g.Contain(gases...)
	return g
}

// NewSubGroup creates a new sub-group with prefix and optional sub-group-level gases.
func (g *Group) NewSubGroup(prefix string, gases ...GasFunc) *Group {
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	return NewGroup(g.prefix+prefix, g.air, gs...)
}

// Contain implements `Air#Contain()`.
func (g *Group) Contain(gases ...GasFunc) {
	g.gases = append(g.gases, gases...)
	// Allow all requests to reach the group as they might get dropped if router
	// doesn't find a match, making none of the group gas process.
	for _, m := range methods {
		g.air.add(m, g.prefix+"*", func(c *Context) error {
			return ErrNotFound
		}, g.gases...)
	}
}

// Get implements `Air#Get()`.
func (g *Group) Get(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(GET, path, handler, gases...)
}

// Post implements `Air#Post()`.
func (g *Group) Post(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(POST, path, handler, gases...)
}

// Put implements `Air#Put()`.
func (g *Group) Put(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(PUT, path, handler, gases...)
}

// Delete implements `Air#Delete()`.
func (g *Group) Delete(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(DELETE, path, handler, gases...)
}

// Any implements `Air#Any()` for sub-routes within the Group.
func (g *Group) Any(path string, handler HandlerFunc, gases ...GasFunc) {
	for _, m := range methods {
		g.add(m, path, handler, gases...)
	}
}

// Static implements `Air#Static()`.
func (g *Group) Static(prefix, root string) {
	g.Get(g.prefix+prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Params[c.ParamNames[0]]))
	})
}

// File implements `Air#File()`.
func (g *Group) File(path, file string) {
	g.Get(g.prefix+path, func(c *Context) error {
		return c.File(file)
	})
}

// add implements `Air#add()`.
func (g *Group) add(method, path string, handler HandlerFunc, gases ...GasFunc) {
	// Combine into a new slice to avoid accidentally passing the same slice for
	// multiple routes, which would lead to later add() calls overwriting the
	// gas from earlier calls.
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	g.air.add(method, g.prefix+path, handler, gs...)
}
<commit_msg>fix: contain gases in group repeatedly<commit_after>package air

import "path"

// Group is a set of sub-routes for a specified route. It can be used for inner
// routes that share a common gas or functionality that should be separate
// from the parent `Air` instance while still inheriting from it.
type Group struct {
	prefix string
	gases  []GasFunc
	air    *Air
}

// NewGroup returns a new router group with prefix and optional group-level gases.
func NewGroup(prefix string, a *Air, gases ...GasFunc) *Group {
	g := &Group{prefix: prefix, air: a}
	g.Contain(gases...)
	// Allow all requests to reach the group as they might get dropped if router
	// doesn't find a match, making none of the group gas process.
	g.air.Any(g.prefix+"*", func(c *Context) error {
		return ErrNotFound
	}, g.gases...)
	return g
}

// NewSubGroup creates a new sub-group with prefix and optional sub-group-level gases.
func (g *Group) NewSubGroup(prefix string, gases ...GasFunc) *Group {
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	return NewGroup(g.prefix+prefix, g.air, gs...)
}

// Contain implements `Air#Contain()`.
func (g *Group) Contain(gases ...GasFunc) {
	g.gases = append(g.gases, gases...)
}

// Get implements `Air#Get()`.
func (g *Group) Get(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(GET, path, handler, gases...)
}

// Post implements `Air#Post()`.
func (g *Group) Post(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(POST, path, handler, gases...)
}

// Put implements `Air#Put()`.
func (g *Group) Put(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(PUT, path, handler, gases...)
}

// Delete implements `Air#Delete()`.
func (g *Group) Delete(path string, handler HandlerFunc, gases ...GasFunc) {
	g.add(DELETE, path, handler, gases...)
}

// Any implements `Air#Any()` for sub-routes within the Group.
func (g *Group) Any(path string, handler HandlerFunc, gases ...GasFunc) {
	for _, m := range methods {
		g.add(m, path, handler, gases...)
	}
}

// Static implements `Air#Static()`.
func (g *Group) Static(prefix, root string) {
	g.Get(g.prefix+prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Params[c.ParamNames[0]]))
	})
}

// File implements `Air#File()`.
func (g *Group) File(path, file string) {
	g.Get(g.prefix+path, func(c *Context) error {
		return c.File(file)
	})
}

// add implements `Air#add()`.
func (g *Group) add(method, path string, handler HandlerFunc, gases ...GasFunc) {
	// Combine into a new slice to avoid accidentally passing the same slice for
	// multiple routes, which would lead to later add() calls overwriting the
	// gas from earlier calls.
	gs := []GasFunc{}
	gs = append(gs, g.gases...)
	gs = append(gs, gases...)
	g.air.add(method, g.prefix+path, handler, gs...)
}
<|endoftext|>


<commit_before>package restpc

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"time"
)

type Request interface {
	HTTP() *http.Request
	RemoteIP() (string, error)
	URL() *url.URL
	Host() string
	HandlerName() string

	Body() ([]byte, error)
	BodyMap() (map[string]interface{}, error)
	BodyTo(model interface{}) error

	GetHeader(string) string

	GetString(key string, sources ...FromX) (*string, error)
	GetStringList(key string, sources ...FromX) ([]string, error)
	GetInt(key string, sources ...FromX) (*int, error)
	GetFloat(key string, sources ...FromX) (*float64, error)
	GetBool(key string, sources ...FromX) (*bool, error)
	GetTime(key string, sources ...FromX) (*time.Time, error)

	FullMap() map[string]interface{}
}

type FromX interface {
	GetString(req Request, key string) (*string, error)
	GetStringList(req Request, key string) ([]string, error)
	GetInt(req Request, key string) (*int, error)
	GetFloat(req Request, key string) (*float64, error)
	GetBool(req Request, key string) (*bool, error)
	GetTime(req Request, key string) (*time.Time, error)
}

var DefaultParamSources = []FromX{
	FromBody,
	FromForm,
	// FromContext, // I don't have any use case for it, enable if you want
	// FromEmpty, // makes every param optional, enable if you want
}

type requestImp struct {
	r           *http.Request // must be set initially
	handlerName string        // must be set initially
	body        []byte
	bodyErr     error
	bodyMap     map[string]interface{}
	bodyMapErr  error
}

func (req *requestImp) HTTP() *http.Request {
	return req.r
}

func (req *requestImp) RemoteIP() (string, error) {
	remoteIp, _, err := net.SplitHostPort(req.r.RemoteAddr)
	if err != nil {
		return "", NewError(
			Internal, "", err,
			"r.RemoteAddr", req.r.RemoteAddr,
		)
	}
	return remoteIp, nil
}

func (req *requestImp) URL() *url.URL {
	u := *req.r.URL
	return &u
}

func (req *requestImp) Host() string {
	return req.r.Host
}

func (req *requestImp) HandlerName() string {
	return req.handlerName
}

func (req *requestImp) Body() ([]byte, error) {
	if req.body != nil {
		return req.body, nil
	}
	if req.bodyErr != nil {
		return nil, req.bodyErr
	}
	if req.r.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.r.Body)
	if err != nil {
		req.bodyErr = err
		log.Println(err)
	}
	req.body = body
	req.r.Body.Close()
	req.r.Body = nil
	return body, nil
}

func (req *requestImp) BodyMap() (map[string]interface{}, error) {
	if req.bodyMap != nil {
		return req.bodyMap, nil
	}
	if req.bodyMapErr != nil {
		return nil, req.bodyMapErr
	}
	data := map[string]interface{}{}
	body, err := req.Body()
	if err != nil {
		req.bodyMapErr = err
		return nil, err
	}
	if len(body) > 0 {
		err = json.Unmarshal(body, &data)
		if err != nil {
			req.bodyMapErr = err
			log.Println(err)
			// return nil, err // FIXME
		}
	}
	req.bodyMap = data
	return data, nil
}

func (req *requestImp) BodyTo(model interface{}) error {
	body, err := req.Body()
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, model)
	if err != nil {
		return NewError(InvalidArgument, "request body is not a valid json", err)
	}
	return nil
}

func (req *requestImp) GetHeader(key string) string {
	return req.r.Header.Get(key)
}

func (req *requestImp) GetString(key string, sources ...FromX) (*string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetString(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetStringList(key string, sources ...FromX) ([]string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetStringList(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetInt(key string, sources ...FromX) (*int, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetInt(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetFloat(key string, sources ...FromX) (*float64, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetFloat(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetBool(key string, sources ...FromX) (*bool, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetBool(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetTime(key string, sources ...FromX) (*time.Time, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetTime(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) HeaderCopy() http.Header {
	header := http.Header{}
	for key, values := range req.r.Header {
		header[key] = values
	}
	return header
}

func (req *requestImp) HeaderStrippedAuth() http.Header {
	header := req.HeaderCopy()
	authHader, ok := header["Authorization"]
	if ok {
		authHaderNew := make([]string, len(authHader))
		for i := 0; i < len(authHader); i++ {
			authHaderNew[i] = "[REMOVED]"
		}
		header["Authorization"] = authHaderNew
	}
	return header
}

func (req *requestImp) FullMap() map[string]interface{} {
	bodyMap, _ := req.BodyMap()
	urlStr := req.URL().String()
	remoteIP, _ := req.RemoteIP()
	return map[string]interface{}{
		"bodyMap":  bodyMap,
		"url":      urlStr,
		"form":     req.r.Form,
		"header":   req.HeaderStrippedAuth(),
		"remoteIP": remoteIP,
	}
}
<commit_msg>add new method: req.GetIntDefault(key, defaultValue)<commit_after>package restpc

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"time"
)

type Request interface {
	HTTP() *http.Request
	RemoteIP() (string, error)
	URL() *url.URL
	Host() string
	HandlerName() string

	Body() ([]byte, error)
	BodyMap() (map[string]interface{}, error)
	BodyTo(model interface{}) error

	GetHeader(string) string

	GetString(key string, sources ...FromX) (*string, error)
	GetStringList(key string, sources ...FromX) ([]string, error)
	GetInt(key string, sources ...FromX) (*int, error)
	GetIntDefault(key string, defaultValue int, sources ...FromX) (int, error)
	GetFloat(key string, sources ...FromX) (*float64, error)
	GetBool(key string, sources ...FromX) (*bool, error)
	GetTime(key string, sources ...FromX) (*time.Time, error)

	FullMap() map[string]interface{}
}

type FromX interface {
	GetString(req Request, key string) (*string, error)
	GetStringList(req Request, key string) ([]string, error)
	GetInt(req Request, key string) (*int, error)
	GetFloat(req Request, key string) (*float64, error)
	GetBool(req Request, key string) (*bool, error)
	GetTime(req Request, key string) (*time.Time, error)
}

var DefaultParamSources = []FromX{
	FromBody,
	FromForm,
	// FromContext, // I don't have any use case for it, enable if you want
	// FromEmpty, // makes every param optional, enable if you want
}

type requestImp struct {
	r           *http.Request // must be set initially
	handlerName string        // must be set initially
	body        []byte
	bodyErr     error
	bodyMap     map[string]interface{}
	bodyMapErr  error
}

func (req *requestImp) HTTP() *http.Request {
	return req.r
}

func (req *requestImp) RemoteIP() (string, error) {
	remoteIp, _, err := net.SplitHostPort(req.r.RemoteAddr)
	if err != nil {
		return "", NewError(
			Internal, "", err,
			"r.RemoteAddr", req.r.RemoteAddr,
		)
	}
	return remoteIp, nil
}

func (req *requestImp) URL() *url.URL {
	u := *req.r.URL
	return &u
}

func (req *requestImp) Host() string {
	return req.r.Host
}

func (req *requestImp) HandlerName() string {
	return req.handlerName
}

func (req *requestImp) Body() ([]byte, error) {
	if req.body != nil {
		return req.body, nil
	}
	if req.bodyErr != nil {
		return nil, req.bodyErr
	}
	if req.r.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.r.Body)
	if err != nil {
		req.bodyErr = err
		log.Println(err)
	}
	req.body = body
	req.r.Body.Close()
	req.r.Body = nil
	return body, nil
}

func (req *requestImp) BodyMap() (map[string]interface{}, error) {
	if req.bodyMap != nil {
		return req.bodyMap, nil
	}
	if req.bodyMapErr != nil {
		return nil, req.bodyMapErr
	}
	data := map[string]interface{}{}
	body, err := req.Body()
	if err != nil {
		req.bodyMapErr = err
		return nil, err
	}
	if len(body) > 0 {
		err = json.Unmarshal(body, &data)
		if err != nil {
			req.bodyMapErr = err
			log.Println(err)
			// return nil, err // FIXME
		}
	}
	req.bodyMap = data
	return data, nil
}

func (req *requestImp) BodyTo(model interface{}) error {
	body, err := req.Body()
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, model)
	if err != nil {
		return NewError(InvalidArgument, "request body is not a valid json", err)
	}
	return nil
}

func (req *requestImp) GetHeader(key string) string {
	return req.r.Header.Get(key)
}

func (req *requestImp) GetString(key string, sources ...FromX) (*string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetString(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetStringList(key string, sources ...FromX) ([]string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetStringList(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetInt(key string, sources ...FromX) (*int, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetInt(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetIntDefault(key string, defaultValue int, sources ...FromX) (int, error) {
	if len(sources) == 0 {
		sources = []FromX{
			FromBody,
			FromForm,
		}
	}
	for _, source := range sources {
		value, err := source.GetInt(req, key)
		if err != nil {
			return defaultValue, err
		}
		if value != nil {
			return *value, nil
		}
	}
	return defaultValue, nil
}

func (req *requestImp) GetFloat(key string, sources ...FromX) (*float64, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetFloat(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetBool(key string, sources ...FromX) (*bool, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetBool(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetTime(key string, sources ...FromX) (*time.Time, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetTime(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) HeaderCopy() http.Header {
	header := http.Header{}
	for key, values := range req.r.Header {
		header[key] = values
	}
	return header
}

func (req *requestImp) HeaderStrippedAuth() http.Header {
	header := req.HeaderCopy()
	authHader, ok := header["Authorization"]
	if ok {
		authHaderNew := make([]string, len(authHader))
		for i := 0; i < len(authHader); i++ {
			authHaderNew[i] = "[REMOVED]"
		}
		header["Authorization"] = authHaderNew
	}
	return header
}

func (req *requestImp) FullMap() map[string]interface{} {
	bodyMap, _ := req.BodyMap()
	urlStr := req.URL().String()
	remoteIP, _ := req.RemoteIP()
	return map[string]interface{}{
		"bodyMap":  bodyMap,
		"url":      urlStr,
		"form":     req.r.Form,
		"header":   req.HeaderStrippedAuth(),
		"remoteIP": remoteIP,
	}
}
<|endoftext|>