<commit_before>package cmdline

import (
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	colorlog "github.com/disorganizer/brig/util/log"
)

func init() {
	log.SetOutput(os.Stderr)

	// Only log the warning severity or above.
	log.SetLevel(log.DebugLevel)

	// Log pretty text
	log.SetFormatter(&colorlog.ColorfulLogFormatter{})
}

func formatGroup(category string) string {
	return strings.ToUpper(category) + " COMMANDS"
}

////////////////////////////
// Commandline definition //
////////////////////////////

// RunCmdline starts a brig commandline tool.
func RunCmdline() int {

	app := cli.NewApp()
	app.Name = "brig"
	app.Usage = "Secure and dezentralized file synchronization"

	//groups
	repoGroup := formatGroup("repository")
	idntGroup := formatGroup("id helper")
	wdirGroup := formatGroup("working")
	advnGroup := formatGroup("advanced")
	miscGroup := formatGroup("misc")

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "nodaemon,n",
			Usage: "Don't run the daemon",
		},
		cli.StringFlag{
			Name:  "password, x",
			Usage: "Supply user password",
			Value: "",
		},
		cli.StringFlag{
			Name:   "path",
			Usage:  "Path of the repository",
			Value:  ".",
			EnvVar: "BRIG_PATH",
		},
	}

	// Commands.
	app.Commands = []cli.Command{
		{
			Name:        "init",
			Category:    repoGroup,
			Usage:       "Initialize an empty repository",
			ArgsUsage:   "<brig-id>",
			Description: "Creates a new brig repository folder and unlocks it.\n   The name of the folder is derivated from the given brig-id.\n   brig-id example: yourname@optionaldomain/ressource",
			Action:      withArgCheck(needAtLeast(1), withExit(handleInit)),
		},
		cli.Command{
			Name:        "open",
			Category:    repoGroup,
			Usage:       "Open an encrypted repository",
			ArgsUsage:   "[--password]",
			Description: "Open a closed (encrypted) brig repository by providing a password",
			Action:      withDaemon(handleOpen, true),
		},
		cli.Command{
			Name:        "close",
			Category:    repoGroup,
			Usage:       "Close an encrypted repository",
			Description: "Encrypt all metadata in the repository and go offline",
			Action:      withDaemon(handleClose, false),
		},
		cli.Command{
			Name:        "history",
			Category:    repoGroup,
			Usage:       "Show the history of the given brig file",
			Action:      withArgCheck(needAtLeast(1), withDaemon(handleHistory, true)),
			Description: "history lists all modifications of a given file",
			ArgsUsage:   "<filename>",
		},
		cli.Command{
			Name:        "net",
			Category:    repoGroup,
			Usage:       "Query and modify network status",
			ArgsUsage:   "[offline | online | status]",
			Description: "Query and modify the connection state to the ipfs network",
			Subcommands: []cli.Command{
				cli.Command{
					Name:   "offline",
					Usage:  "Disconnect from the outside world. The daemon will continue running",
					Action: withDaemon(handleOffline, true),
				},
				cli.Command{
					Name:   "online",
					Usage:  "Connect the daemon to the outside world",
					Action: withDaemon(handleOnline, true),
				},
				cli.Command{
					Name:   "status",
					Usage:  "Check if the daemon is online",
					Action: withDaemon(handleIsOnline, true),
				},
			},
		},

		cli.Command{
			Name:     "remote",
			Category: idntGroup,
			Usage:    "Manage remotes",
			Action:   withDaemon(handleRemoteList, true),
			Subcommands: []cli.Command{
				cli.Command{
					Name:   "add",
					Usage:  "Add a specific remote",
					Action: withArgCheck(needAtLeast(2), withDaemon(handleRemoteAdd, true)),
				},
				cli.Command{
					Name:   "remove",
					Usage:  "Remove a specifc remote",
					Action: withArgCheck(needAtLeast(1), withDaemon(handleRemoteRemove, true)),
				},
				cli.Command{
					Name:   "list",
					Usage:  "List remote status",
					Action: withDaemon(handleRemoteList, true),
				},
				cli.Command{
					Name:   "locate",
					Usage:  "Search a specific remote",
					Action: withArgCheck(needAtLeast(1), withDaemon(handleRemoteLocate, true)),
				},
				cli.Command{
					Name:   "self",
					Usage:  "Print own identity hash",
					Action: withDaemon(handleRemoteSelf, true),
				},
			},
		},
		cli.Command{
			Name:     "tree",
			Usage:    "List files in a tree",
			Category: wdirGroup,
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "depth, d",
					Usage: "Max depth to traverse",
					Value: -1,
				},
			},
			Action: withDaemon(handleTree, true),
		},
		cli.Command{
			Name:     "ls",
			Usage:    "List files",
			Category: wdirGroup,
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "depth, d",
					Usage: "Max depth to traverse",
					Value: -1,
				},
				cli.BoolFlag{
					Name:  "recursive,r",
					Usage: "Allow recursive traverse",
				},
			},
			Action: withDaemon(handleList, true),
		},
		cli.Command{
			Name:     "mkdir",
			Category: wdirGroup,
			Usage:    "Create an empty directory",
			Action:   withArgCheck(needAtLeast(1), withDaemon(handleMkdir, true)),
		},
		cli.Command{
			Name:     "add",
			Category: wdirGroup,
			Usage:    "Transer file into brig's control",
			Action:   withArgCheck(needAtLeast(1), withDaemon(handleAdd, true)),
		},
		cli.Command{
			Name:     "rm",
			Category: wdirGroup,
			Usage:    "Remove the file and optionally old versions of it",
			Action:   withArgCheck(needAtLeast(1), withDaemon(handleRm, true)),
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "recursive,r",
					Usage: "Remove directories recursively",
				},
			},
		},
		cli.Command{
			Name:     "mv",
			Category: wdirGroup,
			Usage:    "Move a file from SOURCE to DEST",
			Action:   withArgCheck(needAtLeast(2), withDaemon(handleMv, true)),
		},
		cli.Command{
			Name:     "cat",
			Category: wdirGroup,
			Usage:    "Write ",
			Action:   withArgCheck(needAtLeast(1), withDaemon(handleCat, true)),
		},
		cli.Command{
			Name:     "daemon",
			Category: advnGroup,
			Usage:    "Manually run the daemon process",
			// Flags
			Action: withExit(handleDaemon),
			Subcommands: []cli.Command{
				cli.Command{
					Name:     "quit",
					Category: advnGroup,
					Usage:    "Manually kill the daemon process",
					Action:   withDaemon(handleDaemonQuit, false),
				},
				cli.Command{
					Name:     "ping",
					Category: advnGroup,
					Usage:    "See if the daemon responds in a timely fashion",
					Action:   withDaemon(handleDaemonPing, false),
				},
				cli.Command{
					Name:     "wait",
					Category: advnGroup,
					Usage:    "Block until the daemon is available",
					Action:   withExit(handleDaemonWait),
				},
			},
		},
		cli.Command{
			Name:     "config",
			Category: miscGroup,
			Usage:    "Access, list and modify configuration values",
			Subcommands: []cli.Command{
				cli.Command{
					Name:   "list",
					Usage:  "Show current config values",
					Action: withExit(withConfig(handleConfigList)),
				},
				cli.Command{
					Name:   "get",
					Usage:  "Connect the daemon to the outside world",
					Action: withExit(withConfig(handleConfigGet)),
				},
				cli.Command{
					Name:   "set",
					Usage:  "Check if the daemon is online",
					Action: withExit(withConfig(handleConfigSet)),
				},
			},
		},
		cli.Command{
			Name:     "mount",
			Category: miscGroup,
			Usage:    "Handle FUSE mountpoints",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "unmount,u",
					Usage: "Unmount the specified directory",
				},
			},
			Action: withArgCheck(needAtLeast(1), withDaemon(handleMount, true)),
		},
	}

	app.Run(os.Args)
	return 0
}
<commit_msg>cmdline/parser.go: Remaining commands cleanup.<commit_after>package cmdline

import (
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	colorlog "github.com/disorganizer/brig/util/log"
)

func init() {
	log.SetOutput(os.Stderr)

	// Only log the warning severity or above.
	log.SetLevel(log.DebugLevel)

	// Log pretty text
	log.SetFormatter(&colorlog.ColorfulLogFormatter{})
}

func formatGroup(category string) string {
	return strings.ToUpper(category) + " COMMANDS"
}

////////////////////////////
// Commandline definition //
////////////////////////////

// RunCmdline starts a brig commandline tool.
func RunCmdline() int {

	app := cli.NewApp()
	app.Name = "brig"
	app.Usage = "Secure and dezentralized file synchronization"

	//groups
	repoGroup := formatGroup("repository")
	wdirGroup := formatGroup("working")
	advnGroup := formatGroup("advanced")
	miscGroup := formatGroup("misc")

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "nodaemon,n",
			Usage: "Don't run the daemon",
		},
		cli.StringFlag{
			Name:  "password, x",
			Usage: "Supply user password",
			Value: "",
		},
		cli.StringFlag{
			Name:   "path",
			Usage:  "Path of the repository",
			Value:  ".",
			EnvVar: "BRIG_PATH",
		},
	}

	// Commands.
	app.Commands = []cli.Command{
		{
			Name:        "init",
			Category:    repoGroup,
			Usage:       "Initialize an empty repository",
			ArgsUsage:   "<brig-id>",
			Description: "Creates a new brig repository folder and unlocks it.\n   The name of the folder is derivated from the given brig-id.\n   brig-id example: yourname@optionaldomain/ressource",
			Action:      withArgCheck(needAtLeast(1), withExit(handleInit)),
		},
		cli.Command{
			Name:        "open",
			Category:    repoGroup,
			Usage:       "Open an encrypted repository",
			ArgsUsage:   "[--password]",
			Description: "Open a closed (encrypted) brig repository by providing a password",
			Action:      withDaemon(handleOpen, true),
		},
		cli.Command{
			Name:        "close",
			Category:    repoGroup,
			Usage:       "Close an encrypted repository",
			Description: "Encrypt all metadata in the repository and go offline",
			Action:      withDaemon(handleClose, false),
		},
		cli.Command{
			Name:        "history",
			Category:    repoGroup,
			Usage:       "Show the history of the given brig file",
			Action:      withArgCheck(needAtLeast(1), withDaemon(handleHistory, true)),
			Description: "history lists all modifications of a given file",
			ArgsUsage:   "<filename>",
		},
		cli.Command{
			Name:        "net",
			Category:    repoGroup,
			Usage:       "Query and modify network status",
			ArgsUsage:   "[offline | online | status]",
			Description: "Query and modify the connection state to the ipfs network",
			Subcommands: []cli.Command{
				cli.Command{
					Name:   "offline",
					Usage:  "Disconnect from the outside world. The daemon will continue running",
					Action: withDaemon(handleOffline, true),
				},
				cli.Command{
					Name:   "online",
					Usage:  "Connect the daemon to the outside world",
					Action: withDaemon(handleOnline, true),
				},
				cli.Command{
					Name:   "status",
					Usage:  "Check if the daemon is online",
					Action: withDaemon(handleIsOnline, true),
				},
			},
		},

		cli.Command{
			Name:        "remote",
			Category:    repoGroup,
			Usage:       "Remote management.",
			ArgsUsage:   "[add | remove | list | locate | self]",
			Description: "Add, remove, list, locate remotes and print own identity",
			Subcommands: []cli.Command{
				cli.Command{
					Name:        "add",
					Usage:       "Add a specific remote",
					ArgsUsage:   "<brig-id> <ipfs-hash>",
					Description: "Adds a specific user (brig-remote-id) with a specific identity (ipfs-hash) to remotes",
					Action:      withArgCheck(needAtLeast(2), withDaemon(handleRemoteAdd, true)),
				},
				cli.Command{
					Name:        "remove",
					Usage:       "Remove a specifc remote",
					ArgsUsage:   "<brig-remote-id>",
					Description: "Removes a specific remote from remotes.",
					Action:      withArgCheck(needAtLeast(1), withDaemon(handleRemoteRemove, true)),
				},
				cli.Command{
					Name:        "list",
					Usage:       "List status of known remotes",
					Description: "Lists all known remotes and their status",
					Action:      withDaemon(handleRemoteList, true),
				},
				cli.Command{
					Name:        "locate",
					Usage:       "Search a specific remote",
					ArgsUsage:   "<brig-remote-id>",
					Description: "Locates all remotes with the given brig-remote-id ",
					Action:      withArgCheck(needAtLeast(1), withDaemon(handleRemoteLocate, true)),
				},
				cli.Command{
					Name:        "self",
					Usage:       "Print identity",
					Description: "Prints the users identity and online status",
					Action:      withDaemon(handleRemoteSelf, true),
				},
			},
		},
		cli.Command{
			Name:        "tree",
			Usage:       "List files in a tree",
			ArgsUsage:   "[/brig-path] [--depth|-d]",
			Description: "Lists all files of a specific brig path in a tree like-manner",
			Category:    wdirGroup,
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "depth, d",
					Usage: "Max depth to traverse",
					Value: -1,
				},
			},
			Action: withDaemon(handleTree, true),
		},
		cli.Command{
			Name:        "ls",
			Usage:       "List files",
			ArgsUsage:   "[/brig-path] [--depth|-d] [--recursive|-r]",
			Description: "Lists all files of a specific brig path in a ls-like manner",
			Category:    wdirGroup,
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "depth, d",
					Usage: "Max depth to traverse",
					Value: -1,
				},
				cli.BoolFlag{
					Name:  "recursive,r",
					Usage: "Allow recursive traverse",
				},
			},
			Action: withDaemon(handleList, true),
		},
		cli.Command{
			Name:        "mkdir",
			Category:    wdirGroup,
			Usage:       "Create an empty directory",
			ArgsUsage:   "</dirname>",
			Description: "Create a empty directory",
			Action:      withArgCheck(needAtLeast(1), withDaemon(handleMkdir, true)),
		},
		cli.Command{
			Name:        "add",
			Category:    wdirGroup,
			Usage:       "Transer file into brig's control",
			ArgsUsage:   "</file>",
			Description: "Add a specific file to the brig repository",
			Action:      withArgCheck(needAtLeast(1), withDaemon(handleAdd, true)),
		},
		cli.Command{
			Name:        "rm",
			Category:    wdirGroup,
			Usage:       "Remove the file and optionally old versions of it",
			ArgsUsage:   "</file> [--recursive|-r]",
			Description: "Remove a spcific file or directory",
			Action:      withArgCheck(needAtLeast(1), withDaemon(handleRm, true)),
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "recursive,r",
					Usage: "Remove directories recursively",
				},
			},
		},
		cli.Command{
			Name:        "mv",
			Category:    wdirGroup,
			Usage:       "Move a specific file",
			ArgsUsage:   "</sourcefile> </destinationfile>",
			Description: "Move a file from SOURCE to DEST",
			Action:      withArgCheck(needAtLeast(2), withDaemon(handleMv, true)),
		},
		cli.Command{
			Name:        "cat",
			Category:    wdirGroup,
			Usage:       "Concatenates a file",
			ArgsUsage:   "</file>",
			Description: "Concatenates files and print them on stdout",
			Action:      withArgCheck(needAtLeast(1), withDaemon(handleCat, true)),
		},
		cli.Command{
			Name:     "daemon",
			Category: advnGroup,
			Usage:    "Manually run the daemon process",
			Subcommands: []cli.Command{
				cli.Command{
					Name:        "launch",
					Category:    advnGroup,
					Usage:       "Start the daemon process",
					Description: "Start the brig daemon process, unlock the repository and go online",
					Action:      withExit(handleDaemon),
				},
				cli.Command{
					Name:        "quit",
					Category:    advnGroup,
					Usage:       "Manually kill the daemon process",
					Description: "Disconnect from ipfs network, shutdown the daemon and lock the repository",
					Action:      withDaemon(handleDaemonQuit, false),
				},
				cli.Command{
					Name:        "ping",
					Category:    advnGroup,
					Usage:       "See if the daemon responds in a timely fashion",
					Description: "Checks if deamon is running and reports the response time",
					Action:      withDaemon(handleDaemonPing, false),
				},
				cli.Command{
					Name:        "wait",
					Category:    advnGroup,
					Usage:       "Block until the daemon is available",
					Description: "Wait blocks until the daemon is available",
					Action:      withExit(handleDaemonWait),
				},
			},
		},
		cli.Command{
			Name:     "config",
			Category: miscGroup,
			Usage:    "Access, list and modify configuration values",
			Subcommands: []cli.Command{
				cli.Command{
					Name:        "list",
					Usage:       "Show current config values",
					Description: "Show the current brig configuration",
					Action:      withExit(withConfig(handleConfigList)),
				},
				cli.Command{
					Name:        "get",
					Usage:       "Get a specific config value",
					Description: "Get a specific config value and print it to stdout",
					Action:      withExit(withConfig(handleConfigGet)),
				},
				cli.Command{
					Name:        "set",
					Usage:       "Set a specific config value",
					Description: "Set a given config option to the given value",
					Action:      withExit(withConfig(handleConfigSet)),
				},
			},
		},
		cli.Command{
			Name:        "mount",
			Category:    miscGroup,
			Usage:       "Mount a brig repository",
			ArgsUsage:   "[--umount|-u] <mountpath>",
			Description: "Mount a brig repository as FUSE filesystem",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "umount,u",
					Usage: "Unmount the specified directory",
				},
			},
			Action: withArgCheck(needAtLeast(1), withDaemon(handleMount, true)),
		},
	}

	app.Run(os.Args)
	return 0
}
<|endoftext|>


<commit_before>package log

import (
	"time"

	"github.com/go-stack/stack"
)

// A Valuer generates a log value. When passed to Context.With in a value
// element (odd indexes), it represents a dynamic value which is re-evaluated
// with each log event.
type Valuer func() interface{}

// bindValues replaces all value elements (odd indexes) containing a Valuer
// with their generated value.
func bindValues(keyvals []interface{}) {
	for i := 1; i < len(keyvals); i += 2 {
		if v, ok := keyvals[i].(Valuer); ok {
			keyvals[i] = v()
		}
	}
}

// containsValuer returns true if any of the value elements (odd indexes)
// contain a Valuer.
func containsValuer(keyvals []interface{}) bool {
	for i := 1; i < len(keyvals); i += 2 {
		if _, ok := keyvals[i].(Valuer); ok {
			return true
		}
	}
	return false
}

// Timestamp returns a Valuer that invokes the underlying function when bound,
// returning a time.Time. Users will probably want to use DefaultTimestamp or
// DefaultTimestampUTC.
func Timestamp(t func() time.Time) Valuer {
	return func() interface{} { return t() }
}

var (
	// DefaultTimestamp is a Valuer that returns the current wallclock time,
	// respecting time zones, when bound.
	DefaultTimestamp Valuer = func() interface{} { return time.Now().Format(time.RFC3339) }

	// DefaultTimestampUTC is a Valuer that returns the current time in UTC
	// when bound.
	DefaultTimestampUTC Valuer = func() interface{} { return time.Now().UTC().Format(time.RFC3339) }
)

// Caller returns a Valuer that returns a file and line from a specified depth
// in the callstack. Users will probably want to use DefaultCaller.
func Caller(depth int) Valuer {
	return func() interface{} { return stack.Caller(depth) }
}

var (
	// DefaultCaller is a Valuer that returns the file and line where the Log
	// method was invoked. It can only be used with log.With.
	DefaultCaller = Caller(3)
)
<commit_msg>Change timestamp value format to nano precision<commit_after>package log

import (
	"time"

	"github.com/go-stack/stack"
)

// A Valuer generates a log value. When passed to Context.With in a value
// element (odd indexes), it represents a dynamic value which is re-evaluated
// with each log event.
type Valuer func() interface{}

// bindValues replaces all value elements (odd indexes) containing a Valuer
// with their generated value.
func bindValues(keyvals []interface{}) {
	for i := 1; i < len(keyvals); i += 2 {
		if v, ok := keyvals[i].(Valuer); ok {
			keyvals[i] = v()
		}
	}
}

// containsValuer returns true if any of the value elements (odd indexes)
// contain a Valuer.
func containsValuer(keyvals []interface{}) bool {
	for i := 1; i < len(keyvals); i += 2 {
		if _, ok := keyvals[i].(Valuer); ok {
			return true
		}
	}
	return false
}

// Timestamp returns a Valuer that invokes the underlying function when bound,
// returning a time.Time. Users will probably want to use DefaultTimestamp or
// DefaultTimestampUTC.
func Timestamp(t func() time.Time) Valuer {
	return func() interface{} { return t() }
}

var (
	// DefaultTimestamp is a Valuer that returns the current wallclock time,
	// respecting time zones, when bound.
	DefaultTimestamp Valuer = func() interface{} { return time.Now().Format(time.RFC3339Nano) }

	// DefaultTimestampUTC is a Valuer that returns the current time in UTC
	// when bound.
	DefaultTimestampUTC Valuer = func() interface{} { return time.Now().UTC().Format(time.RFC3339Nano) }
)

// Caller returns a Valuer that returns a file and line from a specified depth
// in the callstack. Users will probably want to use DefaultCaller.
func Caller(depth int) Valuer {
	return func() interface{} { return stack.Caller(depth) }
}

var (
	// DefaultCaller is a Valuer that returns the file and line where the Log
	// method was invoked. It can only be used with log.With.
	DefaultCaller = Caller(3)
)
<|endoftext|>


<commit_before>/*
Copyright 2015 Gravitational, Inc.

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

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

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

package auth

import (
	"context"
	"crypto/x509"
	"io/ioutil"
	"strings"

	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib"
	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/services"
	"github.com/gravitational/teleport/lib/tlsca"
	"github.com/gravitational/teleport/lib/utils"

	"github.com/gravitational/trace"
)

// LocalRegister is used to generate host keys when a node or proxy is running
// within the same process as the Auth Server and as such, does not need to
// use provisioning tokens.
func LocalRegister(id IdentityID, authServer *AuthServer, additionalPrincipals, dnsNames []string, remoteAddr string) (*Identity, error) {
	// If local registration is happening and no remote address was passed in
	// (which means no advertise IP was set), use localhost.
	if remoteAddr == "" {
		remoteAddr = defaults.Localhost
	}
	keys, err := authServer.GenerateServerKeys(GenerateServerKeysRequest{
		HostID:               id.HostUUID,
		NodeName:             id.NodeName,
		Roles:                teleport.Roles{id.Role},
		AdditionalPrincipals: additionalPrincipals,
		RemoteAddr:           remoteAddr,
		DNSNames:             dnsNames,
		NoCache:              true,
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	identity, err := ReadIdentityFromKeyPair(keys)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return identity, nil
}

// RegisterParams specifies parameters
// for first time register operation with auth server
type RegisterParams struct {
	// DataDir is the data directory
	// storing CA certificate
	DataDir string
	// Token is a secure token to join the cluster
	Token string
	// ID is identity ID
	ID IdentityID
	// Servers is a list of auth servers to dial
	Servers []utils.NetAddr
	// AdditionalPrincipals is a list of additional principals to dial
	AdditionalPrincipals []string
	// DNSNames is a list of DNS names to add to x509 certificate
	DNSNames []string
	// PrivateKey is a PEM encoded private key (not passed to auth servers)
	PrivateKey []byte
	// PublicTLSKey is a server's public key to sign
	PublicTLSKey []byte
	// PublicSSHKey is a server's public SSH key to sign
	PublicSSHKey []byte
	// CipherSuites is a list of cipher suites to use for TLS client connection
	CipherSuites []uint16
	// CAPin is the SKPI hash of the CA used to verify the Auth Server.
	CAPin string
	// CAPath is the path to the CA file.
	CAPath string
	// GetHostCredentials is a client that can fetch host credentials.
	GetHostCredentials HostCredentials
}

// CredGetter is an interface for a client that can be used to get host
// credentials. This interface is needed because lib/client can not be imported
// in lib/auth due to circular imports.
type HostCredentials func(context.Context, string, bool, RegisterUsingTokenRequest) (*PackedKeys, error)

// Register is used to generate host keys when a node or proxy are running on
// different hosts than the auth server. This method requires provisioning
// tokens to prove a valid auth server was used to issue the joining request
// as well as a method for the node to validate the auth server.
func Register(params RegisterParams) (*Identity, error) {
	// Read in the token. The token can either be passed in or come from a file
	// on disk.
	token, err := readToken(params.Token)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// Attempt to register through the auth server, if it fails, try and
	// register through the proxy server.
	ident, err := registerThroughAuth(token, params)
	if err != nil {
		// If no params client was set this is a proxy and fail right away.
		if params.GetHostCredentials == nil {
			log.Debugf("Missing client, failing with error from Auth Server: %v.", err)
			return nil, trace.Wrap(err)
		}

		ident, er := registerThroughProxy(token, params)
		if er != nil {
			return nil, trace.NewAggregate(err, er)
		}

		log.Debugf("Successfully registered through proxy server.")
		return ident, nil
	}

	log.Debugf("Successfully registered through auth server.")
	return ident, nil
}

// registerThroughProxy is used to register through the proxy server.
func registerThroughProxy(token string, params RegisterParams) (*Identity, error) {
	log.Debugf("Attempting to register through proxy server.")

	if len(params.Servers) == 0 {
		return nil, trace.BadParameter("no auth servers set")
	}

	keys, err := params.GetHostCredentials(context.Background(),
		params.Servers[0].String(),
		lib.IsInsecureDevMode(),
		RegisterUsingTokenRequest{
			Token:                token,
			HostID:               params.ID.HostUUID,
			NodeName:             params.ID.NodeName,
			Role:                 params.ID.Role,
			AdditionalPrincipals: params.AdditionalPrincipals,
			DNSNames:             params.DNSNames,
			PublicTLSKey:         params.PublicTLSKey,
			PublicSSHKey:         params.PublicSSHKey,
		})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	keys.Key = params.PrivateKey

	return ReadIdentityFromKeyPair(keys)
}

// registerThroughAuth is used to register through the auth server.
func registerThroughAuth(token string, params RegisterParams) (*Identity, error) {
	log.Debugf("Attempting to register through auth server.")

	var client *Client
	var err error

	// Build a client to the Auth Server. If a CA pin is specified require the
	// Auth Server is validated. Otherwise attempt to use the CA file on disk
	// but if it's not available connect without validating the Auth Server CA.
	switch {
	case params.CAPin != "":
		client, err = pinRegisterClient(params)
	default:
		client, err = insecureRegisterClient(params)
	}
	if err != nil {
		return nil, trace.Wrap(err)
	}
	defer client.Close()

	// Get the SSH and X509 certificates for a node.
	keys, err := client.RegisterUsingToken(RegisterUsingTokenRequest{
		Token:                token,
		HostID:               params.ID.HostUUID,
		NodeName:             params.ID.NodeName,
		Role:                 params.ID.Role,
		AdditionalPrincipals: params.AdditionalPrincipals,
		DNSNames:             params.DNSNames,
		PublicTLSKey:         params.PublicTLSKey,
		PublicSSHKey:         params.PublicSSHKey,
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	keys.Key = params.PrivateKey

	return ReadIdentityFromKeyPair(keys)
}

// insecureRegisterClient attempts to connects to the Auth Server using the
// CA on disk. If no CA is found on disk, Teleport will not verify the Auth
// Server it is connecting to.
func insecureRegisterClient(params RegisterParams) (*Client, error) {
	tlsConfig := utils.TLSConfig(params.CipherSuites)

	cert, err := readCA(params)
	if err != nil && !trace.IsNotFound(err) {
		return nil, trace.Wrap(err)
	}

	// If no CA was found, then create a insecure connection to the Auth Server,
	// otherwise use the CA on disk to validate the Auth Server.
	if trace.IsNotFound(err) {
		tlsConfig.InsecureSkipVerify = true

		log.Warnf("Joining cluster without validating the identity of the Auth " +
			"Server. This may open you up to a Man-In-The-Middle (MITM) attack if an " +
			"attacker can gain privileged network access. To remedy this, use the CA pin " +
			"value provided when join token was generated to validate the identity of " +
			"the Auth Server.")
	} else {
		certPool := x509.NewCertPool()
		certPool.AddCert(cert)
		tlsConfig.RootCAs = certPool

		log.Infof("Joining remote cluster %v, validating connection with certificate on disk.", cert.Subject.CommonName)
	}

	client, err := NewTLSClient(ClientConfig{Addrs: params.Servers, TLS: tlsConfig})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return client, nil
}

// readCA will read in CA that will be used to validate the certificate that
// the Auth Server presents.
func readCA(params RegisterParams) (*x509.Certificate, error) {
	certBytes, err := utils.ReadPath(params.CAPath)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	cert, err := tlsca.ParseCertificatePEM(certBytes)
	if err != nil {
		return nil, trace.Wrap(err, "failed to parse certificate at %v", params.CAPath)
	}
	return cert, nil
}

// pinRegisterClient first connects to the Auth Server using a insecure
// connection to fetch the root CA. If the root CA matches the provided CA
// pin, a connection will be re-established and the root CA will be used to
// validate the certificate presented. If both conditions hold true, then we
// know we are connecting to the expected Auth Server.
func pinRegisterClient(params RegisterParams) (*Client, error) {
	// Build a insecure client to the Auth Server. This is safe because even if
	// an attacker were to MITM this connection the CA pin will not match below.
	tlsConfig := utils.TLSConfig(params.CipherSuites)
	tlsConfig.InsecureSkipVerify = true
	client, err := NewTLSClient(ClientConfig{Addrs: params.Servers, TLS: tlsConfig})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	defer client.Close()

	// Fetch the root CA from the Auth Server. The NOP role has access to the
	// GetClusterCACert endpoint.
	localCA, err := client.GetClusterCACert()
	if err != nil {
		return nil, trace.Wrap(err)
	}
	tlsCA, err := tlsca.ParseCertificatePEM(localCA.TLSCA)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// Check that the SPKI pin matches the CA we fetched over a insecure
	// connection. This makes sure the CA fetched over a insecure connection is
	// in-fact the expected CA.
	err = utils.CheckSPKI(params.CAPin, tlsCA)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	log.Infof("Joining remote cluster %v with CA pin.", tlsCA.Subject.CommonName)

	// Create another client, but this time with the CA provided to validate
	// that the Auth Server was issued a certificate by the same CA.
	tlsConfig = utils.TLSConfig(params.CipherSuites)
	certPool := x509.NewCertPool()
	certPool.AddCert(tlsCA)
	tlsConfig.RootCAs = certPool

	client, err = NewTLSClient(ClientConfig{Addrs: params.Servers, TLS: tlsConfig})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return client, nil
}

// ReRegisterParams specifies parameters for re-registering
// in the cluster (rotating certificates for existing members)
type ReRegisterParams struct {
	// Client is an authenticated client using old credentials
	Client ClientI
	// ID is identity ID
	ID IdentityID
	// AdditionalPrincipals is a list of additional principals to dial
	AdditionalPrincipals []string
	// DNSNames is a list of DNS Names to add to the x509 client certificate
	DNSNames []string
	// PrivateKey is a PEM encoded private key (not passed to auth servers)
	PrivateKey []byte
	// PublicTLSKey is a server's public key to sign
	PublicTLSKey []byte
	// PublicSSHKey is a server's public SSH key to sign
	PublicSSHKey []byte
	// Rotation is the rotation state of the certificate authority
	Rotation services.Rotation
}

// ReRegister renews the certificates and private keys based on the client's existing identity.
func ReRegister(params ReRegisterParams) (*Identity, error) {
	hostID, err := params.ID.HostID()
	if err != nil {
		return nil, trace.Wrap(err)
	}
	keys, err := params.Client.GenerateServerKeys(GenerateServerKeysRequest{
		HostID:               hostID,
		NodeName:             params.ID.NodeName,
		Roles:                teleport.Roles{params.ID.Role},
		AdditionalPrincipals: params.AdditionalPrincipals,
		DNSNames:             params.DNSNames,
		PublicTLSKey:         params.PublicTLSKey,
		PublicSSHKey:         params.PublicSSHKey,
		Rotation:             &params.Rotation,
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	keys.Key = params.PrivateKey

	return ReadIdentityFromKeyPair(keys)
}

func readToken(token string) (string, error) {
	if !strings.HasPrefix(token, "/") {
		return token, nil
	}
	// treat it as a file
	out, err := ioutil.ReadFile(token)
	if err != nil {
		return "", nil
	}
	// trim newlines as tokens in files tend to have newlines
	return strings.TrimSpace(string(out)), nil
}

// PackedKeys is a collection of private key, SSH host certificate
// and TLS certificate and certificate authority issued the certificate
type PackedKeys struct {
	// Key is a private key
	Key []byte `json:"key"`
	// Cert is an SSH host cert
	Cert []byte `json:"cert"`
	// TLSCert is an X509 certificate
	TLSCert []byte `json:"tls_cert"`
	// TLSCACerts is a list of TLS certificate authorities.
	TLSCACerts [][]byte `json:"tls_ca_certs"`
	// SSHCACerts is a list of SSH certificate authorities.
	SSHCACerts [][]byte `json:"ssh_ca_certs"`
}
<commit_msg>Clearer logging when node fails to join cluster.<commit_after>/*
Copyright 2015 Gravitational, Inc.

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

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

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

package auth

import (
	"context"
	"crypto/x509"
	"io/ioutil"
	"strings"

	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib"
	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/services"
	"github.com/gravitational/teleport/lib/tlsca"
	"github.com/gravitational/teleport/lib/utils"

	"github.com/gravitational/trace"
)

// LocalRegister is used to generate host keys when a node or proxy is running
// within the same process as the Auth Server and as such, does not need to
// use provisioning tokens.
func LocalRegister(id IdentityID, authServer *AuthServer, additionalPrincipals, dnsNames []string, remoteAddr string) (*Identity, error) {
	// If local registration is happening and no remote address was passed in
	// (which means no advertise IP was set), use localhost.
	if remoteAddr == "" {
		remoteAddr = defaults.Localhost
	}
	keys, err := authServer.GenerateServerKeys(GenerateServerKeysRequest{
		HostID:               id.HostUUID,
		NodeName:             id.NodeName,
		Roles:                teleport.Roles{id.Role},
		AdditionalPrincipals: additionalPrincipals,
		RemoteAddr:           remoteAddr,
		DNSNames:             dnsNames,
		NoCache:              true,
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	identity, err := ReadIdentityFromKeyPair(keys)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return identity, nil
}

// RegisterParams specifies parameters
// for first time register operation with auth server
type RegisterParams struct {
	// DataDir is the data directory
	// storing CA certificate
	DataDir string
	// Token is a secure token to join the cluster
	Token string
	// ID is identity ID
	ID IdentityID
	// Servers is a list of auth servers to dial
	Servers []utils.NetAddr
	// AdditionalPrincipals is a list of additional principals to dial
	AdditionalPrincipals []string
	// DNSNames is a list of DNS names to add to x509 certificate
	DNSNames []string
	// PrivateKey is a PEM encoded private key (not passed to auth servers)
	PrivateKey []byte
	// PublicTLSKey is a server's public key to sign
	PublicTLSKey []byte
	// PublicSSHKey is a server's public SSH key to sign
	PublicSSHKey []byte
	// CipherSuites is a list of cipher suites to use for TLS client connection
	CipherSuites []uint16
	// CAPin is the SKPI hash of the CA used to verify the Auth Server.
	CAPin string
	// CAPath is the path to the CA file.
	CAPath string
	// GetHostCredentials is a client that can fetch host credentials.
	GetHostCredentials HostCredentials
}

// CredGetter is an interface for a client that can be used to get host
// credentials. This interface is needed because lib/client can not be imported
// in lib/auth due to circular imports.
type HostCredentials func(context.Context, string, bool, RegisterUsingTokenRequest) (*PackedKeys, error)

// Register is used to generate host keys when a node or proxy are running on
// different hosts than the auth server. This method requires provisioning
// tokens to prove a valid auth server was used to issue the joining request
// as well as a method for the node to validate the auth server.
func Register(params RegisterParams) (*Identity, error) {
	// Read in the token. The token can either be passed in or come from a file
	// on disk.
	token, err := readToken(params.Token)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// Attempt to register through the auth server, if it fails, try and
	// register through the proxy server.
	ident, err := registerThroughAuth(token, params)
	if err != nil {
		// If no params client was set this is a proxy and fail right away.
		if params.GetHostCredentials == nil {
			log.Debugf("Missing client, failing with error from Auth Server: %v.", err)
			return nil, trace.Wrap(err)
		}

		ident, err = registerThroughProxy(token, params)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		log.Debugf("Successfully registered through proxy server.")
		return ident, nil
	}

	log.Debugf("Successfully registered through auth server.")
	return ident, nil
}

// registerThroughProxy is used to register through the proxy server.
func registerThroughProxy(token string, params RegisterParams) (*Identity, error) {
	log.Debugf("Attempting to register through proxy server.")

	if len(params.Servers) == 0 {
		return nil, trace.BadParameter("no auth servers set")
	}

	keys, err := params.GetHostCredentials(context.Background(),
		params.Servers[0].String(),
		lib.IsInsecureDevMode(),
		RegisterUsingTokenRequest{
			Token:                token,
			HostID:               params.ID.HostUUID,
			NodeName:             params.ID.NodeName,
			Role:                 params.ID.Role,
			AdditionalPrincipals: params.AdditionalPrincipals,
			DNSNames:             params.DNSNames,
			PublicTLSKey:         params.PublicTLSKey,
			PublicSSHKey:         params.PublicSSHKey,
		})
	if err != nil {
		return nil, trace.Unwrap(err)
	}
	keys.Key = params.PrivateKey

	return ReadIdentityFromKeyPair(keys)
}

// registerThroughAuth is used to register through the auth server.
func registerThroughAuth(token string, params RegisterParams) (*Identity, error) {
	log.Debugf("Attempting to register through auth server.")

	var client *Client
	var err error

	// Build a client to the Auth Server. If a CA pin is specified require the
	// Auth Server is validated. Otherwise attempt to use the CA file on disk
	// but if it's not available connect without validating the Auth Server CA.
	switch {
	case params.CAPin != "":
		client, err = pinRegisterClient(params)
	default:
		client, err = insecureRegisterClient(params)
	}
	if err != nil {
		return nil, trace.Wrap(err)
	}
	defer client.Close()

	// Get the SSH and X509 certificates for a node.
	keys, err := client.RegisterUsingToken(RegisterUsingTokenRequest{
		Token:                token,
		HostID:               params.ID.HostUUID,
		NodeName:             params.ID.NodeName,
		Role:                 params.ID.Role,
		AdditionalPrincipals: params.AdditionalPrincipals,
		DNSNames:             params.DNSNames,
		PublicTLSKey:         params.PublicTLSKey,
		PublicSSHKey:         params.PublicSSHKey,
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	keys.Key = params.PrivateKey

	return ReadIdentityFromKeyPair(keys)
}

// insecureRegisterClient attempts to connects to the Auth Server using the
// CA on disk. If no CA is found on disk, Teleport will not verify the Auth
// Server it is connecting to.
func insecureRegisterClient(params RegisterParams) (*Client, error) {
	tlsConfig := utils.TLSConfig(params.CipherSuites)

	cert, err := readCA(params)
	if err != nil && !trace.IsNotFound(err) {
		return nil, trace.Wrap(err)
	}

	// If no CA was found, then create a insecure connection to the Auth Server,
	// otherwise use the CA on disk to validate the Auth Server.
	if trace.IsNotFound(err) {
		tlsConfig.InsecureSkipVerify = true

		log.Warnf("Joining cluster without validating the identity of the Auth " +
			"Server. This may open you up to a Man-In-The-Middle (MITM) attack if an " +
			"attacker can gain privileged network access. To remedy this, use the CA pin " +
			"value provided when join token was generated to validate the identity of " +
			"the Auth Server.")
	} else {
		certPool := x509.NewCertPool()
		certPool.AddCert(cert)
		tlsConfig.RootCAs = certPool

		log.Infof("Joining remote cluster %v, validating connection with certificate on disk.", cert.Subject.CommonName)
	}

	client, err := NewTLSClient(ClientConfig{Addrs: params.Servers, TLS: tlsConfig})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return client, nil
}

// readCA will read in CA that will be used to validate the certificate that
// the Auth Server presents.
func readCA(params RegisterParams) (*x509.Certificate, error) {
	certBytes, err := utils.ReadPath(params.CAPath)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	cert, err := tlsca.ParseCertificatePEM(certBytes)
	if err != nil {
		return nil, trace.Wrap(err, "failed to parse certificate at %v", params.CAPath)
	}
	return cert, nil
}

// pinRegisterClient first connects to the Auth Server using a insecure
// connection to fetch the root CA. If the root CA matches the provided CA
// pin, a connection will be re-established and the root CA will be used to
// validate the certificate presented. If both conditions hold true, then we
// know we are connecting to the expected Auth Server.
func pinRegisterClient(params RegisterParams) (*Client, error) {
	// Build a insecure client to the Auth Server. This is safe because even if
	// an attacker were to MITM this connection the CA pin will not match below.
	tlsConfig := utils.TLSConfig(params.CipherSuites)
	tlsConfig.InsecureSkipVerify = true
	client, err := NewTLSClient(ClientConfig{Addrs: params.Servers, TLS: tlsConfig})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	defer client.Close()

	// Fetch the root CA from the Auth Server. The NOP role has access to the
	// GetClusterCACert endpoint.
	localCA, err := client.GetClusterCACert()
	if err != nil {
		return nil, trace.Wrap(err)
	}
	tlsCA, err := tlsca.ParseCertificatePEM(localCA.TLSCA)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// Check that the SPKI pin matches the CA we fetched over a insecure
	// connection. This makes sure the CA fetched over a insecure connection is
	// in-fact the expected CA.
	err = utils.CheckSPKI(params.CAPin, tlsCA)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	log.Infof("Joining remote cluster %v with CA pin.", tlsCA.Subject.CommonName)

	// Create another client, but this time with the CA provided to validate
	// that the Auth Server was issued a certificate by the same CA.
	tlsConfig = utils.TLSConfig(params.CipherSuites)
	certPool := x509.NewCertPool()
	certPool.AddCert(tlsCA)
	tlsConfig.RootCAs = certPool

	client, err = NewTLSClient(ClientConfig{Addrs: params.Servers, TLS: tlsConfig})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return client, nil
}

// ReRegisterParams specifies parameters for re-registering
// in the cluster (rotating certificates for existing members)
type ReRegisterParams struct {
	// Client is an authenticated client using old credentials
	Client ClientI
	// ID is identity ID
	ID IdentityID
	// AdditionalPrincipals is a list of additional principals to dial
	AdditionalPrincipals []string
	// DNSNames is a list of DNS Names to add to the x509 client certificate
	DNSNames []string
	// PrivateKey is a PEM encoded private key (not passed to auth servers)
	PrivateKey []byte
	// PublicTLSKey is a server's public key to sign
	PublicTLSKey []byte
	// PublicSSHKey is a server's public SSH key to sign
	PublicSSHKey []byte
	// Rotation is the rotation state of the certificate authority
	Rotation services.Rotation
}

// ReRegister renews the certificates and private keys based on the client's existing identity.
func ReRegister(params ReRegisterParams) (*Identity, error) {
	hostID, err := params.ID.HostID()
	if err != nil {
		return nil, trace.Wrap(err)
	}
	keys, err := params.Client.GenerateServerKeys(GenerateServerKeysRequest{
		HostID:               hostID,
		NodeName:             params.ID.NodeName,
		Roles:                teleport.Roles{params.ID.Role},
		AdditionalPrincipals: params.AdditionalPrincipals,
		DNSNames:             params.DNSNames,
		PublicTLSKey:         params.PublicTLSKey,
		PublicSSHKey:         params.PublicSSHKey,
		Rotation:             &params.Rotation,
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}
	keys.Key = params.PrivateKey

	return ReadIdentityFromKeyPair(keys)
}

func readToken(token string) (string, error) {
	if !strings.HasPrefix(token, "/") {
		return token, nil
	}
	// treat it as a file
	out, err := ioutil.ReadFile(token)
	if err != nil {
		return "", nil
	}
	// trim newlines as tokens in files tend to have newlines
	return strings.TrimSpace(string(out)), nil
}

// PackedKeys is a collection of private key, SSH host certificate
// and TLS certificate and certificate authority issued the certificate
type PackedKeys struct {
	// Key is a private key
	Key []byte `json:"key"`
	// Cert is an SSH host cert
	Cert []byte `json:"cert"`
	// TLSCert is an X509 certificate
	TLSCert []byte `json:"tls_cert"`
	// TLSCACerts is a list of TLS certificate authorities.
	TLSCACerts [][]byte `json:"tls_ca_certs"`
	// SSHCACerts is a list of SSH certificate authorities.
	SSHCACerts [][]byte `json:"ssh_ca_certs"`
}
<|endoftext|>


<commit_before>// Copyright 2017 John Welsh <john.welsh@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

// Package logviewer provides a terminal UI for viewing log streams from multiple sources
package logviewer

import (
	ui "github.com/gizak/termui"
	"bytes"
	"strconv"
	"github.com/gizak/termui/extra"
	"errors"
	"time"
)

const HelpText string = "[h] and [l] to navigate, [t] to show timestamps, [q] to exit"

// A Line represents a single line of log content
type Line struct {
	Log       string
	Timestamp time.Time
}

// A Viewer is used to display log data from one or more sources
type Viewer struct {
	feeds         []*Feed
	tabpane       *extra.Tabpane
	title         *ui.Par
	initialized   bool
	showTimestamp bool
}

// A Feed represents an independent stream of log data
type Feed struct {
	name       string
	logs       []*Line
	maxHistory int
	par        *ui.Par
	viewer     *Viewer
}

// NewViewer initializes a Viewer with a given name
func NewViewer(name string) *Viewer {
	viewer := &Viewer{
		feeds:         []*Feed{},
		tabpane:       extra.NewTabpane(),
		title:         ui.NewPar("[" + name + "](fg-green,fg-bold)   " + HelpText),
		initialized:   false,
		showTimestamp: false,
	}
	viewer.tabpane.Border = true
	viewer.title.Height = 1
	viewer.title.Border = false

	return viewer
}

func newLogFeed(name string, maxHistory int) *Feed {
	feed := Feed{
		name:       name,
		logs:       []*Line{},
		maxHistory: maxHistory,
	}
	return &feed
}

// AddLogFeed adds a new Feed to the Viewer, returning the index of the Feed for use in
// future calls to AddLogLine
func (v*Viewer) AddLogFeed(name string, maxHistory int) *Feed {
	feed := newLogFeed(name, maxHistory)
	feed.viewer = v
	v.feeds = append(v.feeds, feed)

	feed.par = ui.NewPar(" ")
	feed.par.Border = false

	tab := extra.NewTab(feed.name)
	tab.AddBlocks(feed.par)

	v.tabpane.SetTabs(append(v.tabpane.Tabs, *tab)...)
	v.render()
	return feed
}

// RemoveLogFeed removes a given Feed from the Viewer
func (v*Viewer) RemoveLogFeed(f *Feed) error {
	for i := range v.feeds {
		if v.feeds[i] == f {
			// TODO this probably leaks
			tabs := v.tabpane.Tabs
			v.tabpane.SetTabs(append(tabs[:i], tabs[i+1:]...)...)
			v.render()
			return nil
		}
	}
	return errors.New("Feed not found")
}

// AddLogLine adds a Line to a Feed and renders the result in the user's terminal
func (f*Feed) AddLogLine(l *Line) {
	f.logs = append(f.logs, l)
	// TODO circular queue with size of maxHistory
	if len(f.logs) > f.maxHistory {
		over := len(f.logs) - f.maxHistory
		f.logs = f.logs[over:]
	}
	f.updatePar()
	f.viewer.render()
}

func (f*Feed) updatePar() {
	h := f.par.GetHeight()

	var text bytes.Buffer
	logs := f.logs
	if h < len(f.logs) {
		logs = logs[len(f.logs)-h:]
	}
	for i, line := range logs {
		if f.viewer.showTimestamp {
			text.WriteString("[")
			text.WriteString(line.Timestamp.Format("2006-01-02T15:04:05-0700"))
			text.WriteString("] ")
		}
		text.WriteString(line.Log)
		if i != len(logs)-1 {
			text.WriteString("\n")
		}
	}
	f.par.Text = text.String()
}

func (v*Viewer) titleString(width int) string {
	var title bytes.Buffer
	for i, feed := range v.feeds {
		title.WriteString("[")
		title.WriteString(strconv.Itoa(i))
		title.WriteString("]")
		if len(feed.name)+title.Len() > width-3 {

		}
		title.WriteString(feed.name)
		title.WriteString(" ")
		if title.Len() > width-5 {
			title.WriteString("...")
			break
		}
	}
	return title.String()
}

func (v*Viewer) render() {
	if v.initialized {
		v.updateDimensions()
		ui.Body.Align()
		ui.Render(ui.Body)
	}
}

func (v*Viewer) updateFeedDimensions(w, h int) {
	for _, f := range v.feeds {
		f.par.Width = w
		f.par.Height = h
	}
}

// Display starts the viewer. This call will block until the log display is exited by the user
func (v*Viewer) Display() {
	err := ui.Init()
	if err != nil {
		panic(err)
	}
	defer ui.Close()
	v.initialized = true

	ui.Body.AddRows(
		ui.NewRow(
			ui.NewCol(12, 0, v.title)),
		ui.NewRow(
			ui.NewCol(12, 0, v.tabpane)))

	ui.Handle("/sys/wnd/resize", func(ui.Event) {
		v.render()
	})

	ui.Handle("/sys/kbd/q", func(ui.Event) {
		ui.StopLoop()
	})

	ui.Handle("/sys/kbd/l", func(ui.Event) {
		v.tabpane.SetActiveRight()
		v.render()
	})

	ui.Handle("/sys/kbd/h", func(ui.Event) {
		v.tabpane.SetActiveLeft()
		v.render()
	})

	ui.Handle("/sys/kbd/t", func(ui.Event) {
		v.showTimestamp = !v.showTimestamp
		for _, f := range v.feeds {
			f.updatePar()
		}
		v.render()
	})

	v.render()
	ui.Loop()
}

func (v*Viewer) updateDimensions() {
	w := ui.TermWidth()
	h := ui.TermHeight() - v.tabpane.Height
	ui.Body.Width = w
	v.updateFeedDimensions(w, h)
}
<commit_msg>Finished documenting public API<commit_after>// Copyright 2017 John Welsh <john.welsh@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

// Package logviewer provides a terminal based UI for viewing log streams from multiple sources. Implemented using
// @gizak's awesome termui lib
package logviewer

import (
	ui "github.com/gizak/termui"
	"bytes"
	"strconv"
	"github.com/gizak/termui/extra"
	"errors"
	"time"
)

const HelpText string = "[h] and [l] to navigate, [t] to show timestamps, [q] to exit"

// A Line represents a single line of log content
type Line struct {
	Log       string
	Timestamp time.Time
}

// A Viewer is used to display log data from one or more sources
type Viewer struct {
	feeds         []*Feed
	tabpane       *extra.Tabpane
	title         *ui.Par
	initialized   bool
	showTimestamp bool
}

// A Feed represents an independent stream of log data
type Feed struct {
	name       string
	logs       []*Line
	maxHistory int
	par        *ui.Par
	viewer     *Viewer
}

// NewViewer initializes a Viewer with a given name
func NewViewer(name string) *Viewer {
	viewer := &Viewer{
		feeds:         []*Feed{},
		tabpane:       extra.NewTabpane(),
		title:         ui.NewPar("[" + name + "](fg-green,fg-bold)   " + HelpText),
		initialized:   false,
		showTimestamp: false,
	}
	viewer.tabpane.Border = true
	viewer.title.Height = 1
	viewer.title.Border = false

	return viewer
}

func newLogFeed(name string, maxHistory int) *Feed {
	feed := Feed{
		name:       name,
		logs:       []*Line{},
		maxHistory: maxHistory,
	}
	return &feed
}

// AddLogFeed adds a new Feed to the Viewer, returning the index of the Feed for use in
// future calls to AddLogLine
func (v*Viewer) AddLogFeed(name string, maxHistory int) *Feed {
	feed := newLogFeed(name, maxHistory)
	feed.viewer = v
	v.feeds = append(v.feeds, feed)

	feed.par = ui.NewPar(" ")
	feed.par.Border = false

	tab := extra.NewTab(feed.name)
	tab.AddBlocks(feed.par)

	v.tabpane.SetTabs(append(v.tabpane.Tabs, *tab)...)
	v.render()
	return feed
}

// RemoveLogFeed removes a given Feed from the Viewer
func (v*Viewer) RemoveLogFeed(f *Feed) error {
	for i := range v.feeds {
		if v.feeds[i] == f {
			// TODO this probably leaks
			tabs := v.tabpane.Tabs
			v.tabpane.SetTabs(append(tabs[:i], tabs[i+1:]...)...)
			v.render()
			return nil
		}
	}
	return errors.New("Feed not found")
}

// AddLogLine adds a Line to a Feed and renders the result in the user's terminal
func (f*Feed) AddLogLine(l *Line) {
	f.logs = append(f.logs, l)
	// TODO circular queue with size of maxHistory
	if len(f.logs) > f.maxHistory {
		over := len(f.logs) - f.maxHistory
		f.logs = f.logs[over:]
	}
	f.updatePar()
	f.viewer.render()
}

func (f*Feed) updatePar() {
	h := f.par.GetHeight()

	var text bytes.Buffer
	logs := f.logs
	if h < len(f.logs) {
		logs = logs[len(f.logs)-h:]
	}
	for i, line := range logs {
		if f.viewer.showTimestamp {
			text.WriteString("[")
			text.WriteString(line.Timestamp.Format("2006-01-02T15:04:05-0700"))
			text.WriteString("] ")
		}
		text.WriteString(line.Log)
		if i != len(logs)-1 {
			text.WriteString("\n")
		}
	}
	f.par.Text = text.String()
}

func (v*Viewer) titleString(width int) string {
	var title bytes.Buffer
	for i, feed := range v.feeds {
		title.WriteString("[")
		title.WriteString(strconv.Itoa(i))
		title.WriteString("]")
		if len(feed.name)+title.Len() > width-3 {

		}
		title.WriteString(feed.name)
		title.WriteString(" ")
		if title.Len() > width-5 {
			title.WriteString("...")
			break
		}
	}
	return title.String()
}

func (v*Viewer) render() {
	if v.initialized {
		v.updateDimensions()
		ui.Body.Align()
		ui.Render(ui.Body)
	}
}

func (v*Viewer) updateFeedDimensions(w, h int) {
	for _, f := range v.feeds {
		f.par.Width = w
		f.par.Height = h
	}
}

// Display starts the viewer. This call will block until the log display is exited by the user
func (v*Viewer) Display() {
	err := ui.Init()
	if err != nil {
		panic(err)
	}
	defer ui.Close()
	v.initialized = true

	ui.Body.AddRows(
		ui.NewRow(
			ui.NewCol(12, 0, v.title)),
		ui.NewRow(
			ui.NewCol(12, 0, v.tabpane)))

	ui.Handle("/sys/wnd/resize", func(ui.Event) {
		v.render()
	})

	ui.Handle("/sys/kbd/q", func(ui.Event) {
		ui.StopLoop()
	})

	ui.Handle("/sys/kbd/l", func(ui.Event) {
		v.tabpane.SetActiveRight()
		v.render()
	})

	ui.Handle("/sys/kbd/h", func(ui.Event) {
		v.tabpane.SetActiveLeft()
		v.render()
	})

	ui.Handle("/sys/kbd/t", func(ui.Event) {
		v.showTimestamp = !v.showTimestamp
		for _, f := range v.feeds {
			f.updatePar()
		}
		v.render()
	})

	v.render()
	ui.Loop()
}

func (v*Viewer) updateDimensions() {
	w := ui.TermWidth()
	h := ui.TermHeight() - v.tabpane.Height
	ui.Body.Width = w
	v.updateFeedDimensions(w, h)
}
<|endoftext|>


<commit_before>// rsync transport for netbackup
//
// This file is part of netbackup (http://github.com/marcopaganini/netbackup)
// See instructions in the README.md file that accompanies this program.
// rsync transport for netbackup

package transports

import (
	"fmt"
	"github.com/marcopaganini/logger"
	"github.com/marcopaganini/netbackup/config"
	"github.com/marcopaganini/netbackup/execute"
	"os"
	"strings"
)

const (
	rsyncCmd = "rsync"
)

// RsyncTransport is the main structure for the rsync transport.
type RsyncTransport struct {
	Transport
}

// NewRsyncTransport creates a new Transport object for rsync.
func NewRsyncTransport(config *config.Config, ex execute.Executor, log *logger.Logger, dryRun bool) (*RsyncTransport, error) {
	t := &RsyncTransport{}
	t.config = config
	t.log = log
	t.dryRun = dryRun

	// If execute object is nil, create a new one
	t.execute = ex
	if t.execute == nil {
		t.execute = execute.New()
	}

	// Basic config checking
	if err := t.checkConfig(); err != nil {
		return nil, err
	}
	return t, nil
}

// checkConfig performs rsync specific checks in the configuration.
func (r *RsyncTransport) checkConfig() error {
	// Source and dest directories must be set.
	// Either source host or destination host can be set, not both.
	switch {
	case r.config.SourceDir == "":
		return fmt.Errorf("Config error: SourceDir is empty")
	case r.config.DestDir == "":
		return fmt.Errorf("Config error: DestDir is empty")
	case r.config.SourceHost != "" && r.config.DestHost != "":
		return fmt.Errorf("Config error: Cannot have source & dest host set")
	}
	return nil
}

// Run builds the command name and executes it, saving the output to the log
// file requested in the configuration or a default one if none is specified.
// Temporary files with exclusion and inclusion paths are generated, if needed,
// and removed at the end of execution. If dryRun is set, just output the
// command to be executed and the contents of the exclusion and inclusion lists
// to stderr.
func (r *RsyncTransport) Run() error {
	// Create exclude/include lists, if needed
	err := r.createExcludeFile(r.config.Exclude)
	if err != nil {
		return err
	}
	defer os.Remove(r.excludeFile)

	err = r.createIncludeFile(r.config.Include)
	if err != nil {
		return err
	}
	defer os.Remove(r.includeFile)

	// Build the full rsync command line
	cmd := []string{rsyncCmd, "-avXH", "--delete", "--numeric-ids"}

	if r.excludeFile != "" {
		cmd = append(cmd, fmt.Sprintf("--exclude-from=%s", r.excludeFile))
		cmd = append(cmd, "--delete-excluded")
	}
	if r.includeFile != "" {
		cmd = append(cmd, fmt.Sprintf("--include-from=%s", r.includeFile))
	}
	if len(r.config.ExtraArgs) != 0 {
		for _, v := range r.config.ExtraArgs {
			cmd = append(cmd, v)
		}
	}
	// In rsync, the source needs to ends with a slash or the
	// source directory will be created inside the destination.
	// The exception are the cases where the source already ends
	// in a slash (ex: /)
	src := r.buildSource()
	if !strings.HasSuffix(src, "/") {
		src = src + "/"
	}
	cmd = append(cmd, src)
	cmd = append(cmd, r.buildDest())

	r.log.Verbosef(1, "Command: %s\n", strings.Join(cmd, " "))

	// Execute the command
	if !r.dryRun {
		return execute.RunCommand("RSYNC", cmd, r.log, r.execute, nil, nil)
	}
	return nil
}
<commit_msg>Ignore rsync error 24 (some files disappeared during copy).<commit_after>// rsync transport for netbackup
//
// This file is part of netbackup (http://github.com/marcopaganini/netbackup)
// See instructions in the README.md file that accompanies this program.
// rsync transport for netbackup

package transports

import (
	"fmt"
	"github.com/marcopaganini/logger"
	"github.com/marcopaganini/netbackup/config"
	"github.com/marcopaganini/netbackup/execute"
	"os"
	"strings"
)

const (
	rsyncCmd = "rsync"
)

// RsyncTransport is the main structure for the rsync transport.
type RsyncTransport struct {
	Transport
}

// NewRsyncTransport creates a new Transport object for rsync.
func NewRsyncTransport(config *config.Config, ex execute.Executor, log *logger.Logger, dryRun bool) (*RsyncTransport, error) {
	t := &RsyncTransport{}
	t.config = config
	t.log = log
	t.dryRun = dryRun

	// If execute object is nil, create a new one
	t.execute = ex
	if t.execute == nil {
		t.execute = execute.New()
	}

	// Basic config checking
	if err := t.checkConfig(); err != nil {
		return nil, err
	}
	return t, nil
}

// checkConfig performs rsync specific checks in the configuration.
func (r *RsyncTransport) checkConfig() error {
	// Source and dest directories must be set.
	// Either source host or destination host can be set, not both.
	switch {
	case r.config.SourceDir == "":
		return fmt.Errorf("Config error: SourceDir is empty")
	case r.config.DestDir == "":
		return fmt.Errorf("Config error: DestDir is empty")
	case r.config.SourceHost != "" && r.config.DestHost != "":
		return fmt.Errorf("Config error: Cannot have source & dest host set")
	}
	return nil
}

// Run builds the command name and executes it, saving the output to the log
// file requested in the configuration or a default one if none is specified.
// Temporary files with exclusion and inclusion paths are generated, if needed,
// and removed at the end of execution. If dryRun is set, just output the
// command to be executed and the contents of the exclusion and inclusion lists
// to stderr.
func (r *RsyncTransport) Run() error {
	// Create exclude/include lists, if needed
	err := r.createExcludeFile(r.config.Exclude)
	if err != nil {
		return err
	}
	defer os.Remove(r.excludeFile)

	err = r.createIncludeFile(r.config.Include)
	if err != nil {
		return err
	}
	defer os.Remove(r.includeFile)

	// Build the full rsync command line
	cmd := []string{rsyncCmd, "-avXH", "--delete", "--numeric-ids"}

	if r.excludeFile != "" {
		cmd = append(cmd, fmt.Sprintf("--exclude-from=%s", r.excludeFile))
		cmd = append(cmd, "--delete-excluded")
	}
	if r.includeFile != "" {
		cmd = append(cmd, fmt.Sprintf("--include-from=%s", r.includeFile))
	}
	if len(r.config.ExtraArgs) != 0 {
		for _, v := range r.config.ExtraArgs {
			cmd = append(cmd, v)
		}
	}
	// In rsync, the source needs to ends with a slash or the
	// source directory will be created inside the destination.
	// The exception are the cases where the source already ends
	// in a slash (ex: /)
	src := r.buildSource()
	if !strings.HasSuffix(src, "/") {
		src = src + "/"
	}
	cmd = append(cmd, src)
	cmd = append(cmd, r.buildDest())

	r.log.Verbosef(1, "Command: %s\n", strings.Join(cmd, " "))

	// Execute the command
	err = nil
	if !r.dryRun {
		err := execute.RunCommand("RSYNC", cmd, r.log, r.execute, nil, nil)
		if err != nil {
			rc := execute.ExitCode(err)

			// Rsync uses retcode 24 to indicate "some files disappeared during
			// the transfer" which is immaterial for our purposes. Ignore those
			// cases.
			if rc == 24 {
				r.log.Println("Note: rsync returned error 24 (some files disappeared during copy). Ignoring.")
				err = nil
			}
		}
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"image/png"
	"log"
	"os"
	"strings"
	"time"

	"github.com/peterstace/grayt"
)
import (
	"flag"
	"fmt"
)

func main() {

	var (
		out string
		spp int
		cv  float64
	)

	// Set up flags.
	flag.StringVar(&out, "out", "",
		"output file (must end in .png)")
	flag.IntVar(&spp, "spp-limit", 0,
		"samples per pixel stopping point")
	flag.Float64Var(&cv, "cv-limit", 0.0,
		"neighbourhood CV (coefficient of variation) stopping point")
	flag.Parse()

	// Validate and interpret flags.
	if !strings.HasSuffix(out, ".png") {
		log.Fatalf(`%q does not end in ".png"`, out)
	}
	if (spp == 0 && cv == 0) || (spp != 0 && cv != 0) {
		log.Fatalf(`exactly 1 of s and d must be set`)
	}
	var mode mode
	if spp != 0 {
		mode = &fixedSamplesPerPixel{required: spp}
	} else {
		mode = &untilRelativeStdDevBelowThreshold{threshold: cv}
	}

	// Load scene. TODO: load from file.
	scene := CornellBox()

	// TODO: these should come from command line args.
	const (
		pxWide  = 300
		pxHigh  = 300
		totalPx = pxWide * pxHigh
	)
	acc := grayt.NewAccumulator(pxWide, pxHigh)

	run(mode, scene, acc)

	// TODO: image exposure should come from the scene description.
	img := acc.ToImage(1.0)

	// Write image out to file.
	f, err := os.Create(out)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if err := png.Encode(f, img); err != nil {
		log.Fatal(err)
	}
}

func run(mode mode, scene grayt.Scene, acc grayt.Accumulator) {

	wide, high := acc.Dimensions()
	totalPx := wide * high

	startTime := time.Now()
	iteration := 0

	for !mode.stop() {

		grayt.TracerImage(scene, acc)
		iteration++
		cv := acc.NeighbourCoefficientOfVariation()
		mode.finishSample(cv)

		samplesPerSecond := float64(iteration*totalPx) / time.Now().Sub(startTime).Seconds()

		totalSamples := mode.estSamplesPerPixelRequired()
		totalSamplesStr := "??"
		eta := "??"
		if totalSamples >= 0 {
			totalSamplesStr = fmt.Sprintf("%d", totalSamples)
			etaSeconds := float64((totalSamples-iteration)*totalPx) / samplesPerSecond
			eta = fmt.Sprintf("%v", time.Duration(etaSeconds)*time.Second)
		}

		log.Printf("Sample=%d/%s, Samples/sec=%.2e CV=%.4f ETA=%s\n",
			iteration, totalSamplesStr, samplesPerSecond, cv, eta)
	}

	log.Printf("TotalTime=%s", time.Now().Sub(startTime))
}

type mode interface {
	// estSamplesPerPixelRequired is an estimation of the number of total
	// samples per pixel that will be required before the render is completed.
	estSamplesPerPixelRequired() int

	// finishSample signals to the mode that a sample has been finished. The
	// new coefficient of variation should be supplied.
	finishSample(cv float64)

	// stop indicates if the render is complete.
	stop() bool
}

type fixedSamplesPerPixel struct {
	required  int
	completed int
}

func (f *fixedSamplesPerPixel) estSamplesPerPixelRequired() int {
	return f.required
}

func (f *fixedSamplesPerPixel) finishSample(float64) {
	f.completed++
}

func (f *fixedSamplesPerPixel) stop() bool {
	return f.required == f.completed
}

type untilRelativeStdDevBelowThreshold struct {
	threshold        float64
	currentCV        float64
	previousCV       float64
	reducedCVCount   int
	completed        int
	cvDeltaPerSample float64
}

func (u *untilRelativeStdDevBelowThreshold) estSamplesPerPixelRequired() int {
	if u.reducedCVCount < 5 {
		return -1
	}
	more := (u.currentCV - u.threshold) / u.cvDeltaPerSample
	return u.completed + int(more)
}

func (u *untilRelativeStdDevBelowThreshold) finishSample(relStdDev float64) {
	u.currentCV, u.previousCV = relStdDev, u.currentCV
	if u.currentCV < u.previousCV {
		u.reducedCVCount++
	} else {
		u.reducedCVCount = 0
	}
	u.completed++
	u.cvDeltaPerSample = 0.9*u.cvDeltaPerSample + 0.1*(u.previousCV-u.currentCV)
}

func (u *untilRelativeStdDevBelowThreshold) stop() bool {
	return u.reducedCVCount >= 5 && u.currentCV < u.threshold
}
<commit_msg>Width and Height (pixels) comes from cmdline args<commit_after>package main

import (
	"image/png"
	"log"
	"os"
	"strings"
	"time"

	"github.com/peterstace/grayt"
)
import (
	"flag"
	"fmt"
)

func main() {

	var (
		out            string
		spp            int
		cv             float64
		pxWide, pxHigh int
	)

	// Set up flags.
	flag.StringVar(&out, "o", "",
		"output file (must end in .png)")
	flag.IntVar(&spp, "spp", 0,
		"samples per pixel stopping point")
	flag.Float64Var(&cv, "cv", 0.0,
		"neighbourhood CV (coefficient of variation) stopping point")
	flag.IntVar(&pxWide, "w", 0, "width in pixels")
	flag.IntVar(&pxHigh, "h", 0, "height in pixels")
	flag.Parse()

	// Validate and interpret flags.
	if !strings.HasSuffix(out, ".png") {
		flag.Usage()
		log.Fatalf(`%q does not end in ".png"`, out)
	}
	if (spp == 0 && cv == 0) || (spp != 0 && cv != 0) {
		log.Fatalf(`exactly 1 of s and d must be set`)
	}
	var mode mode
	if spp != 0 {
		mode = &fixedSamplesPerPixel{required: spp}
	} else {
		mode = &untilRelativeStdDevBelowThreshold{threshold: cv}
	}
	if pxWide == 0 || pxHigh == 0 {
		flag.Usage()
		log.Fatal("width and height must be set")
	}

	// Load scene. TODO: load from file.
	scene := CornellBox()

	// TODO: these should come from command line args.
	acc := grayt.NewAccumulator(pxWide, pxHigh)

	run(mode, scene, acc)

	// TODO: image exposure should come from the scene description.
	img := acc.ToImage(1.0)

	// Write image out to file.
	f, err := os.Create(out)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if err := png.Encode(f, img); err != nil {
		log.Fatal(err)
	}
}

func run(mode mode, scene grayt.Scene, acc grayt.Accumulator) {

	wide, high := acc.Dimensions()
	totalPx := wide * high

	startTime := time.Now()
	iteration := 0

	for !mode.stop() {

		grayt.TracerImage(scene, acc)
		iteration++
		cv := acc.NeighbourCoefficientOfVariation()
		mode.finishSample(cv)

		samplesPerSecond := float64(iteration*totalPx) / time.Now().Sub(startTime).Seconds()

		totalSamples := mode.estSamplesPerPixelRequired()
		totalSamplesStr := "??"
		eta := "??"
		if totalSamples >= 0 {
			totalSamplesStr = fmt.Sprintf("%d", totalSamples)
			etaSeconds := float64((totalSamples-iteration)*totalPx) / samplesPerSecond
			eta = fmt.Sprintf("%v", time.Duration(etaSeconds)*time.Second)
		}

		log.Printf("Sample=%d/%s, Samples/sec=%.2e CV=%.4f ETA=%s\n",
			iteration, totalSamplesStr, samplesPerSecond, cv, eta)
	}

	log.Printf("TotalTime=%s", time.Now().Sub(startTime))
}

type mode interface {
	// estSamplesPerPixelRequired is an estimation of the number of total
	// samples per pixel that will be required before the render is completed.
	estSamplesPerPixelRequired() int

	// finishSample signals to the mode that a sample has been finished. The
	// new coefficient of variation should be supplied.
	finishSample(cv float64)

	// stop indicates if the render is complete.
	stop() bool
}

type fixedSamplesPerPixel struct {
	required  int
	completed int
}

func (f *fixedSamplesPerPixel) estSamplesPerPixelRequired() int {
	return f.required
}

func (f *fixedSamplesPerPixel) finishSample(float64) {
	f.completed++
}

func (f *fixedSamplesPerPixel) stop() bool {
	return f.required == f.completed
}

type untilRelativeStdDevBelowThreshold struct {
	threshold        float64
	currentCV        float64
	previousCV       float64
	reducedCVCount   int
	completed        int
	cvDeltaPerSample float64
}

func (u *untilRelativeStdDevBelowThreshold) estSamplesPerPixelRequired() int {
	if u.reducedCVCount < 5 {
		return -1
	}
	more := (u.currentCV - u.threshold) / u.cvDeltaPerSample
	return u.completed + int(more)
}

func (u *untilRelativeStdDevBelowThreshold) finishSample(relStdDev float64) {
	u.currentCV, u.previousCV = relStdDev, u.currentCV
	if u.currentCV < u.previousCV {
		u.reducedCVCount++
	} else {
		u.reducedCVCount = 0
	}
	u.completed++
	u.cvDeltaPerSample = 0.9*u.cvDeltaPerSample + 0.1*(u.previousCV-u.currentCV)
}

func (u *untilRelativeStdDevBelowThreshold) stop() bool {
	return u.reducedCVCount >= 5 && u.currentCV < u.threshold
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/exec"
	"testing"

	"fmt"
	"github.com/stretchr/testify/assert"
)

func TestMustHaveTokenEnv(t *testing.T) {
	currentToken := os.Getenv("CLAIMR_TOKEN")
	os.Unsetenv("CLAIMR_TOKEN")
	defer func() { os.Setenv("CLAIMR_TOKEN", currentToken) }()

	cmd := exec.Command("make", "run")
	//cmd.Env = append(os.Environ(), "TEST_MUST_ENV=1")
	err := cmd.Run()
	fmt.Print(err.(*exec.ExitError))
	//if e, ok := err.(*exec.ExitError); ok && !e.Success() {
	//	return
	//}
	assert.EqualError(t, err, "Claimr slack bot token unset. Set CLAIMR_TOKEN to continue.")
}
<commit_msg>Adding tests to main package<commit_after>package main

import (
	"os"
	"testing"

	"github.com/bouk/monkey"
	log "github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
)

func TestEnvironmentToken(t *testing.T) {
	currentEnv := os.Getenv("CLAIMR_TOKEN")
	os.Unsetenv("CLAIMR_TOKEN")
	defer func() { os.Setenv("CLAIMR_TOKEN", currentEnv) }()

	wantMsg := "Claimr slack bot token unset. Set CLAIMR_TOKEN to continue."

	mockLogFatal := func(msg ...interface{}) {
		assert.Equal(t, wantMsg, msg[0])
		panic("log.Fatal called")
	}
	patchLog := monkey.Patch(log.Fatal, mockLogFatal)
	defer patchLog.Unpatch()
	assert.PanicsWithValue(t, "log.Fatal called", main, "log.Fatal was not called")
}
<|endoftext|>


<commit_before>package ghw

import (
    "testing"
)

func TestHost(t *testing.T) {
    host, err := Host()

    if err != nil {
        t.Fatalf("Expected nil error but got %v", err)
    }
    if host == nil {
        t.Fatalf("Expected non-nil host but got nil.")
    }

    mem := host.Memory
    if mem == nil {
        t.Fatalf("Expected non-nil Memory but got nil.")
    }

    tpb := mem.TotalPhysicalBytes
    if tpb < 1 {
        t.Fatalf("Expected >0 total physical memory, but got %d", tpb)
    }

    tub := mem.TotalUsableBytes
    if tub < 1 {
        t.Fatalf("Expected >0 total usable memory, but got %d", tub)
    }

    cpu := host.CPU
    if cpu == nil {
        t.Fatalf("Expected non-nil CPU, but got nil")
    }

    cores := cpu.TotalCores
    if cores < 1 }
        t.Fatalf("Expected >0 total cores, but got %d", cores)
    }

    threads := cpu.TotalThreads
    if threads < 1 }
        t.Fatalf("Expected >0 total threads, but got %d", threads)
    }

    block := host.Block
    if block == nil {
        t.Fatalf("Expected non-nil Block but got nil.")
    }

    blockTpb := block.TotalPhysicalBytes
    if blockTpb < 1 {
        t.Fatalf("Expected >0 total physical block bytes, but got %d", blockTpb)
    }
}
<commit_msg>Should actually run the damn tests :(<commit_after>package ghw

import (
    "testing"
)

func TestHost(t *testing.T) {
    host, err := Host()

    if err != nil {
        t.Fatalf("Expected nil error but got %v", err)
    }
    if host == nil {
        t.Fatalf("Expected non-nil host but got nil.")
    }

    mem := host.Memory
    if mem == nil {
        t.Fatalf("Expected non-nil Memory but got nil.")
    }

    tpb := mem.TotalPhysicalBytes
    if tpb < 1 {
        t.Fatalf("Expected >0 total physical memory, but got %d", tpb)
    }

    tub := mem.TotalUsableBytes
    if tub < 1 {
        t.Fatalf("Expected >0 total usable memory, but got %d", tub)
    }

    cpu := host.CPU
    if cpu == nil {
        t.Fatalf("Expected non-nil CPU, but got nil")
    }

    cores := cpu.TotalCores
    if cores < 1 {
        t.Fatalf("Expected >0 total cores, but got %d", cores)
    }

    threads := cpu.TotalThreads
    if threads < 1 {
        t.Fatalf("Expected >0 total threads, but got %d", threads)
    }

    block := host.Block
    if block == nil {
        t.Fatalf("Expected non-nil Block but got nil.")
    }

    blockTpb := block.TotalPhysicalBytes
    if blockTpb < 1 {
        t.Fatalf("Expected >0 total physical block bytes, but got %d", blockTpb)
    }
}
<|endoftext|>


<commit_before>package main_test

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/verdverm/frisby"
)

func TestSomething(t *testing.T) {
	errs := frisby.Create("Test ping").
		Get("http://localhost:5025/ping").
		Send().
		ExpectStatus(http.StatusOK).
		Errors()

	for _, err := range errs {
		assert.Nil(t, err)
	}
}
<commit_msg>update<commit_after>package main_test

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/verdverm/frisby"
)

func TestPing(t *testing.T) {
	errs := frisby.Create("Test ping").
		Get("http://localhost:5025/ping").
		Send().
		ExpectStatus(http.StatusOK).
		ExpectContent("pong").
		Errors()

	for _, err := range errs {
		assert.Nil(t, err)
	}
}
<|endoftext|>


<commit_before>// +build integration

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"testing"

	"regexp"

	"github.com/elliotchance/c2go/util"
)

var (
	cPath  = "build/a.out"
	goPath = "build/go.out"
	stdin  = "7"
	args   = []string{"some", "args"}
)

type programOut struct {
	stdout bytes.Buffer
	stderr bytes.Buffer
	isZero bool
}

// TestIntegrationScripts tests all programs in the tests directory.
//
// Integration tests are not run by default (only unit tests). These are
// indicated by the build flags at the top of the file. To include integration
// tests use:
//
//     go test -tags=integration
//
// You can also run a single file with:
//
//     go test -tags=integration -run=TestIntegrationScripts/tests/ctype/isalnum.c
//
func TestIntegrationScripts(t *testing.T) {
	testFiles, err := filepath.Glob("tests/*.c")
	if err != nil {
		t.Fatal(err)
	}

	exampleFiles, err := filepath.Glob("examples/*.c")
	if err != nil {
		t.Fatal(err)
	}

	files := append(testFiles, exampleFiles...)

	isVerbose := flag.CommandLine.Lookup("test.v").Value.String() == "true"

	totalTapTests := 0

	for _, file := range files {
		// Create build folder
		os.Mkdir("build/", os.ModePerm)

		t.Run(file, func(t *testing.T) {
			cProgram := programOut{}
			goProgram := programOut{}

			// Compile C.
			out, err := exec.Command("clang", "-lm", "-o", cPath, file).CombinedOutput()
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			// Run C program
			cmd := exec.Command(cPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &cProgram.stdout
			cmd.Stderr = &cProgram.stderr
			err = cmd.Run()
			cProgram.isZero = err == nil

			programArgs := ProgramArgs{
				inputFile:   file,
				outputFile:  "build/main.go",
				packageName: "main",
			}

			// Compile Go
			err = Start(programArgs)
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			buildErr, err := exec.Command("go", "build", "-o", goPath, "build/main.go").CombinedOutput()
			if err != nil {
				t.Fatal(string(buildErr), err)
			}

			// Run Go program
			cmd = exec.Command(goPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &goProgram.stdout
			cmd.Stderr = &goProgram.stderr
			err = cmd.Run()
			goProgram.isZero = err == nil

			// Check for special exit codes that signal that tests have failed.
			if exitError, ok := err.(*exec.ExitError); ok {
				exitStatus := exitError.Sys().(syscall.WaitStatus).ExitStatus()
				if exitStatus == 101 || exitStatus == 102 {
					t.Fatal(goProgram.stdout.String())
				}
			}

			// Check if both exit codes are zero (or non-zero)
			if cProgram.isZero != goProgram.isZero {
				t.Fatalf("Exit statuses did not match.\n" +
					util.ShowDiff(cProgram.stdout.String(),
						goProgram.stdout.String()),
				)
			}

			// Check stderr
			if cProgram.stderr.String() != goProgram.stderr.String() {
				t.Fatalf("Expected %q, Got: %q",
					cProgram.stderr.String(),
					goProgram.stderr.String())
			}

			// Check stdout
			if cProgram.stdout.String() != goProgram.stdout.String() {
				t.Fatalf(util.ShowDiff(cProgram.stdout.String(),
					goProgram.stdout.String()))
			}

			// If this is not an example we will extact the number of tests run.
			if strings.Index(file, "examples/") == -1 && isVerbose {
				firstLine := strings.Split(goProgram.stdout.String(), "\n")[0]

				matches := regexp.MustCompile(`1\.\.(\d+)`).
					FindStringSubmatch(firstLine)
				if len(matches) == 0 {
					t.Fatalf("Test did not output tap: %s", file)
				}

				fmt.Printf("TAP: # %s: %s tests\n", file, matches[1])
				totalTapTests += util.Atoi(matches[1])
			}
		})
	}

	if isVerbose {
		fmt.Printf("TAP: # Total tests: %d\n", totalTapTests)
	}
}

func TestStartPreprocess(t *testing.T) {
	// temp dir
	tempDir := os.TempDir()

	// create temp file with garantee
	// wrong file body
	tempFile, err := newTempFile(tempDir, "c2go", "preprocess.c")
	if err != nil {
		t.Errorf("Cannot create temp file for execute test")
	}
	defer os.Remove(tempFile.Name())

	fmt.Fprintf(tempFile, "#include <AbsoluteWrongInclude.h>\nint main(void){\nwrong();\n}")

	err = tempFile.Close()
	if err != nil {
		t.Errorf("Cannot close the temp file")
	}

	var args ProgramArgs
	args.inputFile = tempFile.Name()

	err = Start(args)
	if err == nil {
		t.Errorf("Cannot test preprocess of application")
	}
}

func TestGoPath(t *testing.T) {
	gopath := "GOPATH"

	existEnv := os.Getenv(gopath)
	if existEnv == "" {
		t.Errorf("$GOPATH is not set")
	}

	// return env.var.
	defer func() {
		err := os.Setenv(gopath, existEnv)
		if err != nil {
			t.Errorf("Cannot restore the value of $GOPATH")
		}
	}()

	// reset value of env.var.
	err := os.Setenv(gopath, "")
	if err != nil {
		t.Errorf("Cannot set value of $GOPATH")
	}

	// testing
	err = Start(ProgramArgs{})
	if err == nil {
		t.Errorf(err.Error())
	}
}
<commit_msg>Remove build folder afer integration test<commit_after>// +build integration

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"testing"

	"regexp"

	"github.com/elliotchance/c2go/util"
)

var (
	cPath  = "build/a.out"
	goPath = "build/go.out"
	stdin  = "7"
	args   = []string{"some", "args"}
)

type programOut struct {
	stdout bytes.Buffer
	stderr bytes.Buffer
	isZero bool
}

// TestIntegrationScripts tests all programs in the tests directory.
//
// Integration tests are not run by default (only unit tests). These are
// indicated by the build flags at the top of the file. To include integration
// tests use:
//
//     go test -tags=integration
//
// You can also run a single file with:
//
//     go test -tags=integration -run=TestIntegrationScripts/tests/ctype/isalnum.c
//
func TestIntegrationScripts(t *testing.T) {
	testFiles, err := filepath.Glob("tests/*.c")
	if err != nil {
		t.Fatal(err)
	}

	exampleFiles, err := filepath.Glob("examples/*.c")
	if err != nil {
		t.Fatal(err)
	}

	files := append(testFiles, exampleFiles...)

	isVerbose := flag.CommandLine.Lookup("test.v").Value.String() == "true"

	totalTapTests := 0

	// Create build folder
	os.Mkdir("build/", os.ModePerm)
	defer os.RemoveAll("build")

	for _, file := range files {
		t.Run(file, func(t *testing.T) {
			cProgram := programOut{}
			goProgram := programOut{}

			// Compile C.
			out, err := exec.Command("clang", "-lm", "-o", cPath, file).CombinedOutput()
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			// Run C program
			cmd := exec.Command(cPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &cProgram.stdout
			cmd.Stderr = &cProgram.stderr
			err = cmd.Run()
			cProgram.isZero = err == nil

			programArgs := ProgramArgs{
				inputFile:   file,
				outputFile:  "build/main.go",
				packageName: "main",
			}

			// Compile Go
			err = Start(programArgs)
			if err != nil {
				t.Fatalf("error: %s\n%s", err, out)
			}

			buildErr, err := exec.Command("go", "build", "-o", goPath, "build/main.go").CombinedOutput()
			if err != nil {
				t.Fatal(string(buildErr), err)
			}

			// Run Go program
			cmd = exec.Command(goPath, args...)
			cmd.Stdin = strings.NewReader(stdin)
			cmd.Stdout = &goProgram.stdout
			cmd.Stderr = &goProgram.stderr
			err = cmd.Run()
			goProgram.isZero = err == nil

			// Check for special exit codes that signal that tests have failed.
			if exitError, ok := err.(*exec.ExitError); ok {
				exitStatus := exitError.Sys().(syscall.WaitStatus).ExitStatus()
				if exitStatus == 101 || exitStatus == 102 {
					t.Fatal(goProgram.stdout.String())
				}
			}

			// Check if both exit codes are zero (or non-zero)
			if cProgram.isZero != goProgram.isZero {
				t.Fatalf("Exit statuses did not match.\n" +
					util.ShowDiff(cProgram.stdout.String(),
						goProgram.stdout.String()),
				)
			}

			// Check stderr
			if cProgram.stderr.String() != goProgram.stderr.String() {
				t.Fatalf("Expected %q, Got: %q",
					cProgram.stderr.String(),
					goProgram.stderr.String())
			}

			// Check stdout
			if cProgram.stdout.String() != goProgram.stdout.String() {
				t.Fatalf(util.ShowDiff(cProgram.stdout.String(),
					goProgram.stdout.String()))
			}

			// If this is not an example we will extact the number of tests run.
			if strings.Index(file, "examples/") == -1 && isVerbose {
				firstLine := strings.Split(goProgram.stdout.String(), "\n")[0]

				matches := regexp.MustCompile(`1\.\.(\d+)`).
					FindStringSubmatch(firstLine)
				if len(matches) == 0 {
					t.Fatalf("Test did not output tap: %s", file)
				}

				fmt.Printf("TAP: # %s: %s tests\n", file, matches[1])
				totalTapTests += util.Atoi(matches[1])
			}
		})
	}

	if isVerbose {
		fmt.Printf("TAP: # Total tests: %d\n", totalTapTests)
	}
}

func TestStartPreprocess(t *testing.T) {
	// temp dir
	tempDir := os.TempDir()

	// create temp file with garantee
	// wrong file body
	tempFile, err := newTempFile(tempDir, "c2go", "preprocess.c")
	if err != nil {
		t.Errorf("Cannot create temp file for execute test")
	}
	defer os.Remove(tempFile.Name())

	fmt.Fprintf(tempFile, "#include <AbsoluteWrongInclude.h>\nint main(void){\nwrong();\n}")

	err = tempFile.Close()
	if err != nil {
		t.Errorf("Cannot close the temp file")
	}

	var args ProgramArgs
	args.inputFile = tempFile.Name()

	err = Start(args)
	if err == nil {
		t.Errorf("Cannot test preprocess of application")
	}
}

func TestGoPath(t *testing.T) {
	gopath := "GOPATH"

	existEnv := os.Getenv(gopath)
	if existEnv == "" {
		t.Errorf("$GOPATH is not set")
	}

	// return env.var.
	defer func() {
		err := os.Setenv(gopath, existEnv)
		if err != nil {
			t.Errorf("Cannot restore the value of $GOPATH")
		}
	}()

	// reset value of env.var.
	err := os.Setenv(gopath, "")
	if err != nil {
		t.Errorf("Cannot set value of $GOPATH")
	}

	// testing
	err = Start(ProgramArgs{})
	if err == nil {
		t.Errorf(err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/suite"
)

const testImagesFolder = "./test/images"

type FakeExternalServerTestSuite struct {
	suite.Suite
	fakeServer    *http.Server
	subjectServer *httptest.Server
}

func (suite *FakeExternalServerTestSuite) SetupSuite() {
	suite.fakeServer = &http.Server{Addr: ":8081", Handler: http.FileServer(http.Dir(testImagesFolder))}
	go func() {
		suite.fakeServer.ListenAndServe()
		// http.ListenAndServe(":8081", suite.fakeServer)
	}()
}

func TestFakeExternalServerTestSuite(t *testing.T) {
	suite.Run(t, new(FakeExternalServerTestSuite))
}

func (suite *FakeExternalServerTestSuite) SetupTest() {
	suite.subjectServer = httptest.NewServer(GetApp())
	// defer suite.subjectServer.Close()
}

func (suite *FakeExternalServerTestSuite) TestExample() {
	res, err := http.Get(fmt.Sprintf("%s/%s",
		suite.subjectServer.URL, "http://localhost:8081/oi.txt"))
	if err != nil {
		log.Fatal(err)
	}

	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	suite.Equal(404, res.StatusCode, "status code should be 404")
	suite.Equal(string(body), "\n", "they should be equal")
}

func (suite *FakeExternalServerTestSuite) TearDownTest() {
	suite.subjectServer.Close()
}

func (suite *FakeExternalServerTestSuite) TearDownSuite() {
	if err := suite.fakeServer.Shutdown(nil); err != nil {
		panic(err)
	}
}
<commit_msg>Use a png file instead of txt on tests<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/suite"
)

const testImagesFolder = "./test/images"

type FakeExternalServerTestSuite struct {
	suite.Suite
	fakeServer    *http.Server
	subjectServer *httptest.Server
}

func (suite *FakeExternalServerTestSuite) SetupSuite() {
	suite.fakeServer = &http.Server{Addr: ":8081", Handler: http.FileServer(http.Dir(testImagesFolder))}
	go func() {
		suite.fakeServer.ListenAndServe()
	}()
}

func TestFakeExternalServerTestSuite(t *testing.T) {
	suite.Run(t, new(FakeExternalServerTestSuite))
}

func (suite *FakeExternalServerTestSuite) SetupTest() {
	suite.subjectServer = httptest.NewServer(GetApp())
}

func (suite *FakeExternalServerTestSuite) TestExample() {
	res, err := http.Get(fmt.Sprintf("%s/%s",
		suite.subjectServer.URL, "http://localhost:8081/oi.png"))
	if err != nil {
		log.Fatal(err)
	}

	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	suite.Equal(404, res.StatusCode, "status code should be 404")
	suite.Equal(string(body), "\n", "they should be equal")
}

func (suite *FakeExternalServerTestSuite) TearDownTest() {
	suite.subjectServer.Close()
}

func (suite *FakeExternalServerTestSuite) TearDownSuite() {
	if err := suite.fakeServer.Shutdown(nil); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func TestClientOptions(t *testing.T) {
	opts := clientOptions{
        cmdOptions: &cmdOptions{
	        OptEndpoint: "http://www.example.com/mt/mt-data-api.cgi",
	        OptApiVersion: "1",
	        OptClientId: "go-test",
	        OptUsername: "Melody",
        },
        PasswordData: "password",
	}

	if opts.Endpoint() != "http://www.example.com/mt/mt-data-api.cgi" {
		t.Errorf("got %q", opts.Endpoint())
	}

	if opts.ApiVersion() != "1" {
		t.Errorf("got %q", opts.ApiVersion())
	}

	if opts.ClientId() != "go-test" {
		t.Errorf("got %q", opts.ClientId())
	}

	if opts.Username() != "Melody" {
		t.Errorf("got %q", opts.Username())
	}

	if opts.Password() != "password" {
		t.Errorf("got %q", opts.Password())
	}
}
<commit_msg>Fix format.<commit_after>package main

import (
	"testing"
)

func TestClientOptions(t *testing.T) {
	opts := clientOptions{
		cmdOptions: &cmdOptions{
			OptEndpoint:   "http://www.example.com/mt/mt-data-api.cgi",
			OptApiVersion: "1",
			OptClientId:   "go-test",
			OptUsername:   "Melody",
		},
		PasswordData: "password",
	}

	if opts.Endpoint() != "http://www.example.com/mt/mt-data-api.cgi" {
		t.Errorf("got %q", opts.Endpoint())
	}

	if opts.ApiVersion() != "1" {
		t.Errorf("got %q", opts.ApiVersion())
	}

	if opts.ClientId() != "go-test" {
		t.Errorf("got %q", opts.ClientId())
	}

	if opts.Username() != "Melody" {
		t.Errorf("got %q", opts.Username())
	}

	if opts.Password() != "password" {
		t.Errorf("got %q", opts.Password())
	}
}
<|endoftext|>