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

// The os package provides a platform-independent interface to operating
// system functionality.  The design is Unix-like.
package os

import (
	"syscall"
)

func epipecheck(file *File, e int) {
	if e == syscall.EPIPE {
		file.nepipe++
		if file.nepipe >= 10 {
			Exit(syscall.EPIPE)
		}
	} else {
		file.nepipe = 0
	}
}

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an Error, if any.
func Pipe() (r *File, w *File, err Error) {
	var p [2]int

	// See ../syscall/exec.go for description of lock.
	syscall.ForkLock.RLock()
	e := syscall.Pipe(p[0:])
	if iserror(e) {
		syscall.ForkLock.RUnlock()
		return nil, nil, NewSyscallError("pipe", e)
	}
	syscall.CloseOnExec(p[0])
	syscall.CloseOnExec(p[1])
	syscall.ForkLock.RUnlock()

	return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
}

// Stat returns a FileInfo structure describing the named file and an error, if any.
// If name names a valid symbolic link, the returned FileInfo describes
// the file pointed at by the link and has fi.FollowedSymlink set to true.
// If name names an invalid symbolic link, the returned FileInfo describes
// the link itself and has fi.FollowedSymlink set to false.
func Stat(name string) (fi *FileInfo, err Error) {
	var lstat, stat syscall.Stat_t
	e := syscall.Lstat(name, &lstat)
	if iserror(e) {
		return nil, &PathError{"stat", name, Errno(e)}
	}
	statp := &lstat
	if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
		e := syscall.Stat(name, &stat)
		if !iserror(e) {
			statp = &stat
		}
	}
	return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
}

// Lstat returns the FileInfo structure describing the named file and an
// error, if any.  If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
func Lstat(name string) (fi *FileInfo, err Error) {
	var stat syscall.Stat_t
	e := syscall.Lstat(name, &stat)
	if iserror(e) {
		return nil, &PathError{"lstat", name, Errno(e)}
	}
	return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
}

// Remove removes the named file or directory.
func Remove(name string) Error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if !iserror(e) {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if !iserror(e1) {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	// For windows syscall.ENOTDIR is set
	// to syscall.ERROR_DIRECTORY, hopefully it should
	// do the trick.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, Errno(e)}
}

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op    string
	Old   string
	New   string
	Error Error
}

func (e *LinkError) String() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String()
}

// Link creates a hard link.
func Link(oldname, newname string) Error {
	e := syscall.Link(oldname, newname)
	if iserror(e) {
		return &LinkError{"link", oldname, newname, Errno(e)}
	}
	return nil
}

// Symlink creates a symbolic link.
func Symlink(oldname, newname string) Error {
	e := syscall.Symlink(oldname, newname)
	if iserror(e) {
		return &LinkError{"symlink", oldname, newname, Errno(e)}
	}
	return nil
}

// Readlink reads the contents of a symbolic link: the destination of
// the link.  It returns the contents and an Error, if any.
func Readlink(name string) (string, Error) {
	for len := 128; ; len *= 2 {
		b := make([]byte, len)
		n, e := syscall.Readlink(name, b)
		if iserror(e) {
			return "", &PathError{"readlink", name, Errno(e)}
		}
		if n < len {
			return string(b[0:n]), nil
		}
	}
	// Silence 6g.
	return "", nil
}

// Rename renames a file.
func Rename(oldname, newname string) Error {
	e := syscall.Rename(oldname, newname)
	if iserror(e) {
		return &LinkError{"rename", oldname, newname, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
func Chmod(name string, mode uint32) Error {
	if e := syscall.Chmod(name, mode); iserror(e) {
		return &PathError{"chmod", name, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the file to mode.
func (f *File) Chmod(mode uint32) Error {
	if e := syscall.Fchmod(f.fd, mode); iserror(e) {
		return &PathError{"chmod", f.name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
func Chown(name string, uid, gid int) Error {
	if e := syscall.Chown(name, uid, gid); iserror(e) {
		return &PathError{"chown", name, Errno(e)}
	}
	return nil
}

// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
func Lchown(name string, uid, gid int) Error {
	if e := syscall.Lchown(name, uid, gid); iserror(e) {
		return &PathError{"lchown", name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
func (f *File) Chown(uid, gid int) Error {
	if e := syscall.Fchown(f.fd, uid, gid); iserror(e) {
		return &PathError{"chown", f.name, Errno(e)}
	}
	return nil
}

// Truncate changes the size of the file.
// It does not change the I/O offset.
func (f *File) Truncate(size int64) Error {
	if e := syscall.Ftruncate(f.fd, size); iserror(e) {
		return &PathError{"truncate", f.name, Errno(e)}
	}
	return nil
}

// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
func (file *File) Sync() (err Error) {
	if file == nil {
		return EINVAL
	}
	if e := syscall.Fsync(file.fd); iserror(e) {
		return NewSyscallError("fsync", e)
	}
	return nil
}

// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
//
// The argument times are in nanoseconds, although the underlying
// filesystem may truncate or round the values to a more
// coarse time unit.
func Chtimes(name string, atime_ns int64, mtime_ns int64) Error {
	var utimes [2]syscall.Timeval
	utimes[0] = syscall.NsecToTimeval(atime_ns)
	utimes[1] = syscall.NsecToTimeval(mtime_ns)
	if e := syscall.Utimes(name, utimes[0:]); iserror(e) {
		return &PathError{"chtimes", name, Errno(e)}
	}
	return nil
}
<commit_msg>os: turn EPIPE exit into panic<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The os package provides a platform-independent interface to operating
// system functionality.  The design is Unix-like.
package os

import (
	"syscall"
)

func epipecheck(file *File, e int) {
	if e == syscall.EPIPE {
		file.nepipe++
		if file.nepipe >= 10 {
			panic("os.File Write: caller keeps writing after too many EPIPE errors")
		}
	} else {
		file.nepipe = 0
	}
}

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an Error, if any.
func Pipe() (r *File, w *File, err Error) {
	var p [2]int

	// See ../syscall/exec.go for description of lock.
	syscall.ForkLock.RLock()
	e := syscall.Pipe(p[0:])
	if iserror(e) {
		syscall.ForkLock.RUnlock()
		return nil, nil, NewSyscallError("pipe", e)
	}
	syscall.CloseOnExec(p[0])
	syscall.CloseOnExec(p[1])
	syscall.ForkLock.RUnlock()

	return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
}

// Stat returns a FileInfo structure describing the named file and an error, if any.
// If name names a valid symbolic link, the returned FileInfo describes
// the file pointed at by the link and has fi.FollowedSymlink set to true.
// If name names an invalid symbolic link, the returned FileInfo describes
// the link itself and has fi.FollowedSymlink set to false.
func Stat(name string) (fi *FileInfo, err Error) {
	var lstat, stat syscall.Stat_t
	e := syscall.Lstat(name, &lstat)
	if iserror(e) {
		return nil, &PathError{"stat", name, Errno(e)}
	}
	statp := &lstat
	if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
		e := syscall.Stat(name, &stat)
		if !iserror(e) {
			statp = &stat
		}
	}
	return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
}

// Lstat returns the FileInfo structure describing the named file and an
// error, if any.  If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
func Lstat(name string) (fi *FileInfo, err Error) {
	var stat syscall.Stat_t
	e := syscall.Lstat(name, &stat)
	if iserror(e) {
		return nil, &PathError{"lstat", name, Errno(e)}
	}
	return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
}

// Remove removes the named file or directory.
func Remove(name string) Error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if !iserror(e) {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if !iserror(e1) {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	// For windows syscall.ENOTDIR is set
	// to syscall.ERROR_DIRECTORY, hopefully it should
	// do the trick.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, Errno(e)}
}

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op    string
	Old   string
	New   string
	Error Error
}

func (e *LinkError) String() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String()
}

// Link creates a hard link.
func Link(oldname, newname string) Error {
	e := syscall.Link(oldname, newname)
	if iserror(e) {
		return &LinkError{"link", oldname, newname, Errno(e)}
	}
	return nil
}

// Symlink creates a symbolic link.
func Symlink(oldname, newname string) Error {
	e := syscall.Symlink(oldname, newname)
	if iserror(e) {
		return &LinkError{"symlink", oldname, newname, Errno(e)}
	}
	return nil
}

// Readlink reads the contents of a symbolic link: the destination of
// the link.  It returns the contents and an Error, if any.
func Readlink(name string) (string, Error) {
	for len := 128; ; len *= 2 {
		b := make([]byte, len)
		n, e := syscall.Readlink(name, b)
		if iserror(e) {
			return "", &PathError{"readlink", name, Errno(e)}
		}
		if n < len {
			return string(b[0:n]), nil
		}
	}
	// Silence 6g.
	return "", nil
}

// Rename renames a file.
func Rename(oldname, newname string) Error {
	e := syscall.Rename(oldname, newname)
	if iserror(e) {
		return &LinkError{"rename", oldname, newname, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
func Chmod(name string, mode uint32) Error {
	if e := syscall.Chmod(name, mode); iserror(e) {
		return &PathError{"chmod", name, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the file to mode.
func (f *File) Chmod(mode uint32) Error {
	if e := syscall.Fchmod(f.fd, mode); iserror(e) {
		return &PathError{"chmod", f.name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
func Chown(name string, uid, gid int) Error {
	if e := syscall.Chown(name, uid, gid); iserror(e) {
		return &PathError{"chown", name, Errno(e)}
	}
	return nil
}

// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
func Lchown(name string, uid, gid int) Error {
	if e := syscall.Lchown(name, uid, gid); iserror(e) {
		return &PathError{"lchown", name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
func (f *File) Chown(uid, gid int) Error {
	if e := syscall.Fchown(f.fd, uid, gid); iserror(e) {
		return &PathError{"chown", f.name, Errno(e)}
	}
	return nil
}

// Truncate changes the size of the file.
// It does not change the I/O offset.
func (f *File) Truncate(size int64) Error {
	if e := syscall.Ftruncate(f.fd, size); iserror(e) {
		return &PathError{"truncate", f.name, Errno(e)}
	}
	return nil
}

// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
func (file *File) Sync() (err Error) {
	if file == nil {
		return EINVAL
	}
	if e := syscall.Fsync(file.fd); iserror(e) {
		return NewSyscallError("fsync", e)
	}
	return nil
}

// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
//
// The argument times are in nanoseconds, although the underlying
// filesystem may truncate or round the values to a more
// coarse time unit.
func Chtimes(name string, atime_ns int64, mtime_ns int64) Error {
	var utimes [2]syscall.Timeval
	utimes[0] = syscall.NsecToTimeval(atime_ns)
	utimes[1] = syscall.NsecToTimeval(mtime_ns)
	if e := syscall.Utimes(name, utimes[0:]); iserror(e) {
		return &PathError{"chtimes", name, Errno(e)}
	}
	return nil
}
<|endoftext|>


<commit_before>package peer

type liveness struct {
	timeout int64
	prev    int64
	ival    int64
	times   map[string]int64
	self    string
	shun    chan<- string
}

func (lv *liveness) check(t int64) {
	if t > lv.prev+lv.ival {
		n := t - lv.timeout
		for addr, s := range lv.times {
			if n > s && addr != lv.self {
				lv.times[addr] = 0, false
				lv.shun <- addr
			}
		}
		lv.prev = t
	}
}
<commit_msg>log shuns<commit_after>package peer

import (
	"log"
)

type liveness struct {
	timeout int64
	prev    int64
	ival    int64
	times   map[string]int64
	self    string
	shun    chan<- string
}

func (lv *liveness) check(t int64) {
	if t > lv.prev+lv.ival {
		n := t - lv.timeout
		for addr, s := range lv.times {
			if n > s && addr != lv.self {
				lv.times[addr] = 0, false
				log.Printf("shunning addr=%s", addr)
				lv.shun <- addr
			}
		}
		lv.prev = t
	}
}
<|endoftext|>


<commit_before>package scamp

import "errors"
import "net"
import "crypto/tls"
import "crypto/rand"
import "crypto/rsa"
import "encoding/base64"
import "encoding/json"
import "fmt"
import "bytes"
import "io/ioutil"

type ServiceActionFunc func(Request,*Session)
type ServiceAction struct {
	callback ServiceActionFunc
	crudTags string
	version  int
}

type Service struct {
	serviceSpec   string
	name          string
	humanName     string

	listener      net.Listener
	listenerIP    net.IP
	listenerPort  int

	actions       map[string]*ServiceAction
	sessChan      (chan *Session)
	isRunning     bool
	openConns     []*Connection

	cert          tls.Certificate
	pemCert       []byte // just a copy of what was read off disk at tls cert load time
}

func NewService(serviceSpec string, humanName string) (serv *Service, err error){
	if len(humanName) > 18 {
		err = fmt.Errorf("name `%s` is too long, must be less than 18 bytes", humanName)
		return
	}

	serv = new(Service)
	serv.serviceSpec = serviceSpec
	serv.humanName = humanName
	serv.generateRandomName()

	serv.actions = make(map[string]*ServiceAction)
	serv.sessChan = make(chan *Session, 100)

	crtPath := defaultConfig.ServiceCertPath(serv.humanName)
	keyPath := defaultConfig.ServiceKeyPath(serv.humanName)

	if crtPath == nil || keyPath == nil {
		err = fmt.Errorf( "could not find valid crt/key pair for service %s (`%s`,`%s`)", serv.name, crtPath, keyPath )
		return
	}

	// Load keypair for tls socket library to use
	serv.cert, err = tls.LoadX509KeyPair( string(crtPath), string(keyPath) )
	if err != nil {
		return
	}

	// Load cert in to memory for announce packet writing
	serv.pemCert, err = ioutil.ReadFile(string(crtPath))
	if err != nil {
		return
	}
	serv.pemCert = bytes.TrimSpace(serv.pemCert)

	// Finally, get ready for incoming requests
	err = serv.listen()
	if err != nil {
		return
	}

	return
}

func (serv *Service)generateRandomName() {
	randBytes := make([]byte, 18, 18)
	read,err := rand.Read(randBytes)
	if err != nil {
		err = fmt.Errorf("could not generate all rand bytes needed. only read %d of 18", read)
		return
	}
	base64RandBytes := base64.StdEncoding.EncodeToString(randBytes)

	var buffer bytes.Buffer
	buffer.WriteString(serv.humanName)
	buffer.WriteString("-")
	buffer.WriteString(base64RandBytes[0:])
	serv.name = string(buffer.Bytes())
}

// TODO: port discovery and interface/IP discovery should happen here
// important to set values so announce packets are correct
func (serv *Service)listen() (err error) {
	config := &tls.Config{
		Certificates: []tls.Certificate{ serv.cert },
	}

	Info.Printf("starting service on %s", serv.serviceSpec)
	serv.listener,err = tls.Listen("tcp", serv.serviceSpec, config)
	if err != nil {
		return err
	}

  // TODO: get listenerIP to return 127.0.0.1 or something other than '::'/nil
  // serv.listenerIP = serv.listener.Addr().(*net.TCPAddr).IP
  serv.listenerIP, err = IPForAnnouncePacket()
  if err != nil {
  	return
  }

	serv.listenerPort = serv.listener.Addr().(*net.TCPAddr).Port

	return
}

func (serv *Service)Register(name string, callback ServiceActionFunc) (err error) {
	if serv.isRunning {
		return errors.New("cannot register handlers while server is running")
	}

	serv.actions[name] = &ServiceAction {
		callback: callback,
		version: 1,
	}

	return
}

func (serv *Service)Run() {
	go serv.RouteSessions()

	for {
		netConn,err := serv.listener.Accept()
		if err != nil {
			Info.Printf("accept returned error. exiting service Run()")
			break
		}

		var tlsConn (*tls.Conn) = (netConn).(*tls.Conn)
		if tlsConn == nil {
			Error.Fatalf("could not create tlsConn")
			break
		}

		conn,err := newConnection(tlsConn, serv.sessChan)
		if err != nil {
			Error.Fatalf("error with new connection: `%s`", err)
			break
		}
		serv.openConns = append(serv.openConns, conn)

		go conn.packetRouter(false, true)
	}

	close(serv.sessChan)
}

// Spawn a router for each new session received over sessChan
func (serv *Service)RouteSessions() (err error){

	for newSess := range serv.sessChan {

		// if !stillOpen {
		// 	Trace.Printf("sessChan was closed. server is probably shutting down.")
		// 	break
		// }

		go func(){
			var action *ServiceAction

			request,err := newSess.RecvRequest()
			if err != nil {
				return
			}

			action = serv.actions[request.Action]
			if action != nil {
				action.callback(request, newSess)
			} else {
				Error.Printf("unknown action `%s`", request.Action)
				// TODO: need to respond with 'unknown action'
			}
		}()
	}

	return
}

func (serv *Service)Stop(){
	// Sometimes we Stop() before service after service has been init but before it is started
	// The usual case is a bad config in another plugin
	if serv.listener != nil {
		serv.listener.Close()
	}
	for _,conn := range serv.openConns {
		conn.Close()
	}
}

func (serv *Service)MarshalText() (b []byte, err error){
	var buf bytes.Buffer

	serviceProxy := ServiceAsServiceProxy(serv)
	classRecord,err := json.Marshal(&serviceProxy)
	if err != nil {
		return
	}

	sig, err := SignSHA256(classRecord, serv.cert.PrivateKey.(*rsa.PrivateKey))
	if err != nil {
		return
	}

	buf.Write(classRecord)
	buf.WriteString("\n\n")
	buf.Write(serv.pemCert)
	buf.WriteString("\n\n")
	buf.WriteString(sig)

	b = buf.Bytes()
	return
}<commit_msg>try getting the IP from the service's listener socket instead of reprobing interfaces<commit_after>package scamp

import "errors"
import "net"
import "crypto/tls"
import "crypto/rand"
import "crypto/rsa"
import "encoding/base64"
import "encoding/json"
import "fmt"
import "bytes"
import "io/ioutil"

type ServiceActionFunc func(Request,*Session)
type ServiceAction struct {
	callback ServiceActionFunc
	crudTags string
	version  int
}

type Service struct {
	serviceSpec   string
	name          string
	humanName     string

	listener      net.Listener
	listenerIP    net.IP
	listenerPort  int

	actions       map[string]*ServiceAction
	sessChan      (chan *Session)
	isRunning     bool
	openConns     []*Connection

	cert          tls.Certificate
	pemCert       []byte // just a copy of what was read off disk at tls cert load time
}

func NewService(serviceSpec string, humanName string) (serv *Service, err error){
	if len(humanName) > 18 {
		err = fmt.Errorf("name `%s` is too long, must be less than 18 bytes", humanName)
		return
	}

	serv = new(Service)
	serv.serviceSpec = serviceSpec
	serv.humanName = humanName
	serv.generateRandomName()

	serv.actions = make(map[string]*ServiceAction)
	serv.sessChan = make(chan *Session, 100)

	crtPath := defaultConfig.ServiceCertPath(serv.humanName)
	keyPath := defaultConfig.ServiceKeyPath(serv.humanName)

	if crtPath == nil || keyPath == nil {
		err = fmt.Errorf( "could not find valid crt/key pair for service %s (`%s`,`%s`)", serv.name, crtPath, keyPath )
		return
	}

	// Load keypair for tls socket library to use
	serv.cert, err = tls.LoadX509KeyPair( string(crtPath), string(keyPath) )
	if err != nil {
		return
	}

	// Load cert in to memory for announce packet writing
	serv.pemCert, err = ioutil.ReadFile(string(crtPath))
	if err != nil {
		return
	}
	serv.pemCert = bytes.TrimSpace(serv.pemCert)

	// Finally, get ready for incoming requests
	err = serv.listen()
	if err != nil {
		return
	}

	return
}

func (serv *Service)generateRandomName() {
	randBytes := make([]byte, 18, 18)
	read,err := rand.Read(randBytes)
	if err != nil {
		err = fmt.Errorf("could not generate all rand bytes needed. only read %d of 18", read)
		return
	}
	base64RandBytes := base64.StdEncoding.EncodeToString(randBytes)

	var buffer bytes.Buffer
	buffer.WriteString(serv.humanName)
	buffer.WriteString("-")
	buffer.WriteString(base64RandBytes[0:])
	serv.name = string(buffer.Bytes())
}

// TODO: port discovery and interface/IP discovery should happen here
// important to set values so announce packets are correct
func (serv *Service)listen() (err error) {
	config := &tls.Config{
		Certificates: []tls.Certificate{ serv.cert },
	}

	Info.Printf("starting service on %s", serv.serviceSpec)
	serv.listener,err = tls.Listen("tcp", serv.serviceSpec, config)
	if err != nil {
		return err
	}

  // TODO: get listenerIP to return 127.0.0.1 or something other than '::'/nil
  serv.listenerIP = serv.listener.Addr().(*net.TCPAddr).IP
  Trace.Printf("serv.listenerIP: `%s`", serv.listenerIP)
  // serv.listenerIP, err = IPForAnnouncePacket()
  if err != nil {
  	return
  }

	serv.listenerPort = serv.listener.Addr().(*net.TCPAddr).Port

	return
}

func (serv *Service)Register(name string, callback ServiceActionFunc) (err error) {
	if serv.isRunning {
		return errors.New("cannot register handlers while server is running")
	}

	serv.actions[name] = &ServiceAction {
		callback: callback,
		version: 1,
	}

	return
}

func (serv *Service)Run() {
	go serv.RouteSessions()

	for {
		netConn,err := serv.listener.Accept()
		if err != nil {
			Info.Printf("exiting service service Run(): `%s`", err)
			break
		}

		var tlsConn (*tls.Conn) = (netConn).(*tls.Conn)
		if tlsConn == nil {
			Error.Fatalf("could not create tlsConn")
			break
		}

		conn,err := newConnection(tlsConn, serv.sessChan)
		if err != nil {
			Error.Fatalf("error with new connection: `%s`", err)
			break
		}
		serv.openConns = append(serv.openConns, conn)

		go conn.packetRouter(false, true)
	}

	close(serv.sessChan)
}

// Spawn a router for each new session received over sessChan
func (serv *Service)RouteSessions() (err error){

	for newSess := range serv.sessChan {

		// if !stillOpen {
		// 	Trace.Printf("sessChan was closed. server is probably shutting down.")
		// 	break
		// }

		go func(){
			var action *ServiceAction

			request,err := newSess.RecvRequest()
			if err != nil {
				return
			}

			action = serv.actions[request.Action]
			if action != nil {
				action.callback(request, newSess)
			} else {
				Error.Printf("unknown action `%s`", request.Action)
				// TODO: need to respond with 'unknown action'
			}
		}()
	}

	return
}

func (serv *Service)Stop(){
	// Sometimes we Stop() before service after service has been init but before it is started
	// The usual case is a bad config in another plugin
	if serv.listener != nil {
		serv.listener.Close()
	}
	for _,conn := range serv.openConns {
		conn.Close()
	}
}

func (serv *Service)MarshalText() (b []byte, err error){
	var buf bytes.Buffer

	serviceProxy := ServiceAsServiceProxy(serv)
	classRecord,err := json.Marshal(&serviceProxy)
	if err != nil {
		return
	}

	sig, err := SignSHA256(classRecord, serv.cert.PrivateKey.(*rsa.PrivateKey))
	if err != nil {
		return
	}

	buf.Write(classRecord)
	buf.WriteString("\n\n")
	buf.Write(serv.pemCert)
	buf.WriteString("\n\n")
	buf.WriteString(sig)

	b = buf.Bytes()
	return
}<|endoftext|>


<commit_before><commit_msg>recover test logic<commit_after><|endoftext|>


<commit_before>package dos

import "syscall"

type FindFiles struct {
	handle syscall.Handle
	data1  syscall.Win32finddata
}

func FindFirst(pattern string) (*FindFiles, error) {
	pattern16, err := syscall.UTF16PtrFromString(pattern)
	if err != nil {
		return nil, err
	}
	this := new(FindFiles)
	this.handle, err = syscall.FindFirstFile(pattern16, &this.data1)
	if err != nil {
		return nil, err
	}
	return this, nil
}

func (this *FindFiles) Name() string {
	return syscall.UTF16ToString(this.data1.FileName[:])
}

func (this *FindFiles) FindNext() error {
	return syscall.FindNextFile(this.handle, &this.data1)
}

func (this *FindFiles) Close() {
	syscall.FindClose(this.handle)
}

func (this *FindFiles) Attribute() uint32 {
	return this.data1.FileAttributes
}

func (this *FindFiles) IsDir() bool {
	return (this.Attribute() & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0
}

func (this *FindFiles) IsHidden() bool {
	return (this.Attribute() & syscall.FILE_ATTRIBUTE_HIDDEN) != 0
}

func DirName(path string) string {
	lastroot := -1
	for i, i_end := 0, len(path); i < i_end; i++ {
		switch path[i] {
		case '\\', '/', ':':
			lastroot = i
		}
	}
	if lastroot >= 0 {
		return path[0:(lastroot + 1)]
	} else {
		return ""
	}
}
<commit_msg>PowerUp FindFiles for ls<commit_after>package dos

import (
	"os"
	"syscall"
	"time"
)

type FindFiles struct {
	handle syscall.Handle
	data1  syscall.Win32finddata
}

func (this *FindFiles) Clone() *FindFiles {
	return &FindFiles{this.handle, this.data1}
}

func FindFirst(pattern string) (*FindFiles, error) {
	pattern16, err := syscall.UTF16PtrFromString(pattern)
	if err != nil {
		return nil, err
	}
	this := new(FindFiles)
	this.handle, err = syscall.FindFirstFile(pattern16, &this.data1)
	if err != nil {
		return nil, err
	}
	return this, nil
}

func (this *FindFiles) Name() string {
	return syscall.UTF16ToString(this.data1.FileName[:])
}

func (this *FindFiles) Size() int64 {
	return int64((this.data1.FileSizeHigh << 32) | this.data1.FileSizeLow)
}

func (this *FindFiles) ModTime() time.Time {
	return time.Unix(0, this.data1.LastWriteTime.Nanoseconds())
}

func (this *FindFiles) Mode() os.FileMode {
	m := os.FileMode(0444)
	if this.IsDir() {
		m |= 0111 | os.ModeDir
	}
	if !this.IsReadOnly() {
		m |= 0222
	}
	return m
}

func (this *FindFiles) Sys() interface{} {
	return this.data1
}

func (this *FindFiles) FindNext() error {
	return syscall.FindNextFile(this.handle, &this.data1)
}

func (this *FindFiles) Close() {
	syscall.FindClose(this.handle)
}

func (this *FindFiles) Attribute() uint32 {
	return this.data1.FileAttributes
}

func (this *FindFiles) IsReparsePoint() bool {
	return (this.Attribute() & FILE_ATTRIBUTE_REPARSE_POINT) != 0
}

func (this *FindFiles) IsReadOnly() bool {
	return (this.Attribute() & syscall.FILE_ATTRIBUTE_READONLY) != 0
}

func (this *FindFiles) IsExecutable() bool {
	return IsExecutableSuffix(this.Name())
}

func (this *FindFiles) IsDir() bool {
	return (this.Attribute() & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0
}

func (this *FindFiles) IsHidden() bool {
	return (this.Attribute() & syscall.FILE_ATTRIBUTE_HIDDEN) != 0
}

func (this *FindFiles) IsSystem() bool {
	return (this.Attribute() & syscall.FILE_ATTRIBUTE_SYSTEM) != 0
}

func DirName(path string) string {
	lastroot := -1
	for i, i_end := 0, len(path); i < i_end; i++ {
		switch path[i] {
		case '\\', '/', ':':
			lastroot = i
		}
	}
	if lastroot >= 0 {
		return path[0:(lastroot + 1)]
	} else {
		return ""
	}
}

func ForFiles(pattern string, callback func(*FindFiles) bool) error {
	this, err := FindFirst(pattern)
	if err != nil {
		return err
	}
	defer this.Close()
	for {
		if !callback(this.Clone()) {
			return nil
		}
		if err := this.FindNext(); err != nil {
			return nil
		}
	}
}
<|endoftext|>


<commit_before>package drain

import (
	"fmt"
	"net"
	"time"

  "github.com/DataDog/datadog-agent/pkg/logs/config"
	"github.com/DataDog/datadog-agent/pkg/logs/sender"

	"github.com/nanopack/logvac/core"
)

// Datadog drain implements the publisher interface for publishing logs to datadog.
type Datadog struct {
  ID        string // app ID or name
  Key       string // datadog api key
	manager   *sender.ConnectionManager
	conn      net.Conn
}

// NewDatadogClient creates a new mist publisher
func NewDatadogClient(id, key string) (*Datadog, error) {
  // emulate the server config
  config := config.NewServerConfig("intake.logs.datadoghq.com", 10516, true)

  // initialize a connection manager
	manager := sender.NewConnectionManager(config, "")

  // establish a connection
	conn := manager.NewConnection()

	return &Datadog{
    ID:       id,
    Key:      key,
    manager:  manager,
    conn:     conn,
  }, nil
}

// Init initializes a connection to mist
func (p Datadog) Init() error {

	// add drain
	logvac.AddDrain("datadog", p.Publish)

	return nil
}

// Publish utilizes mist's Publish to "drain" a log message
func (p *Datadog) Publish(msg logvac.Message) {
  // keep track of the attempts
	msg.PubTries++

  // re-establish the connection if it's been closed
	if p.conn == nil {
    fmt.Println("Establishing connection with datadog endpoint")
		p.conn = p.manager.NewConnection() // doesn't block (don't goroutine call to Publish)
	}

  // generate the payload for this entry
  payload := formatDataDogMessage(msg, p.ID, p.Key)

  // send the payload
	_, err := p.conn.Write(payload)
	if err != nil {
    fmt.Println("Failed to send payload")
    // it's possible the connection is bad, so let's close it
		p.manager.CloseConnection(p.conn)
		p.conn = nil

    // let's try to send it again (at least 3 times)
		if msg.PubTries <= 3 {
      // give it a sec, networks are fickle
			time.Sleep(2 * time.Second)
      // retry!
			p.Publish(msg)
		}
	}
}

// Close closes the connection to datadog.
func (p *Datadog) Close() error {
	p.manager.CloseConnection(p.conn)
	return nil
}

// format the syslog message, prefixed with the datadog api key
func formatDataDogMessage(msg logvac.Message, id, key string) []byte {
  // format the date in the proper syslog format
  date := fmt.Sprintf("%s %02d %02d:%02d:%02d",
    msg.Time.Month().String()[:3],
    msg.Time.Day(),
    msg.Time.Hour(),
    msg.Time.Minute(),
    msg.Time.Second())

  // prefix the app id to the hostname identifier
  hostname := fmt.Sprintf("%s.%s", id, msg.Id)

  // extract the first tag as the tag (ie: nginx[access])
  tag := msg.Tag[0]

  // the final message
  message := fmt.Sprintf("%s <%d>%s %s %s: %s\n",
    key, msg.Priority, date, hostname, tag, msg.Content)

  // return the message as a byte array
  return []byte(message)
}
<commit_msg>Log the packet details for datadog<commit_after>package drain

import (
	"fmt"
	"net"
	"time"

  "github.com/DataDog/datadog-agent/pkg/logs/config"
	"github.com/DataDog/datadog-agent/pkg/logs/sender"

	"github.com/nanopack/logvac/core"
)

// Datadog drain implements the publisher interface for publishing logs to datadog.
type Datadog struct {
  ID        string // app ID or name
  Key       string // datadog api key
	manager   *sender.ConnectionManager
	conn      net.Conn
}

// NewDatadogClient creates a new mist publisher
func NewDatadogClient(id, key string) (*Datadog, error) {
  // emulate the server config
  config := config.NewServerConfig("intake.logs.datadoghq.com", 10516, true)

  // initialize a connection manager
	manager := sender.NewConnectionManager(config, "")

  // establish a connection
	conn := manager.NewConnection()

	return &Datadog{
    ID:       id,
    Key:      key,
    manager:  manager,
    conn:     conn,
  }, nil
}

// Init initializes a connection to mist
func (p Datadog) Init() error {

	// add drain
	logvac.AddDrain("datadog", p.Publish)

	return nil
}

// Publish utilizes mist's Publish to "drain" a log message
func (p *Datadog) Publish(msg logvac.Message) {
  // keep track of the attempts
	msg.PubTries++

  // re-establish the connection if it's been closed
	if p.conn == nil {
    fmt.Println("Establishing connection with datadog endpoint")
		p.conn = p.manager.NewConnection() // doesn't block (don't goroutine call to Publish)
	}

  // generate the payload for this entry
  payload := formatDataDogMessage(msg, p.ID, p.Key)
  fmt.Println(string(payload[:]))

  // send the payload
	_, err := p.conn.Write(payload)
	if err != nil {
    fmt.Println("Failed to send payload")
    // it's possible the connection is bad, so let's close it
		p.manager.CloseConnection(p.conn)
		p.conn = nil

    // let's try to send it again (at least 3 times)
		if msg.PubTries <= 3 {
      // give it a sec, networks are fickle
			time.Sleep(2 * time.Second)
      // retry!
			p.Publish(msg)
		}
	}
}

// Close closes the connection to datadog.
func (p *Datadog) Close() error {
	p.manager.CloseConnection(p.conn)
	return nil
}

// format the syslog message, prefixed with the datadog api key
func formatDataDogMessage(msg logvac.Message, id, key string) []byte {
  // format the date in the proper syslog format
  date := fmt.Sprintf("%s %02d %02d:%02d:%02d",
    msg.Time.Month().String()[:3],
    msg.Time.Day(),
    msg.Time.Hour(),
    msg.Time.Minute(),
    msg.Time.Second())

  // prefix the app id to the hostname identifier
  hostname := fmt.Sprintf("%s.%s", id, msg.Id)

  // extract the first tag as the tag (ie: nginx[access])
  tag := msg.Tag[0]

  // the final message
  message := fmt.Sprintf("%s <%d>%s %s %s: %s\n",
    key, msg.Priority, date, hostname, tag, msg.Content)

  // return the message as a byte array
  return []byte(message)
}
<|endoftext|>


<commit_before>package drivers

import (
	"log"
	"strings"

	"github.com/mijia/modelq/drivers/mysql"
	"github.com/mijia/modelq/gmq"
)

type MysqlDriver struct{}

func (m MysqlDriver) LoadDatabaseSchema(dsnString, schema, tableNames string) (DbSchema, error) {
	log.Printf("[MySQL Driver] Start to load tables schema from db, %s, tables=%s", schema, tableNames)
	db, err := gmq.Open("mysql", m.useInformationSchema(dsnString, schema))
	if err != nil {
		return nil, err
	}
	defer db.Close()

	if err = db.Ping(); err != nil {
		return nil, err
	}

	dbSchema := make(DbSchema)
	if err = m.queryColumns(db, schema, tableNames, dbSchema); err != nil {
		return nil, err
	}
	log.Printf("[MySQL Driver] Loaded schema data of %d tables from db[%s]", len(dbSchema), schema)
	return dbSchema, nil
}

func (m MysqlDriver) dataType(colDataType string) string {
	kFieldTypes := map[string]string{
		"bigint":   "int64",
		"int":      "int",
		"tinyint":  "int",
		"char":     "string",
		"varchar":  "string",
		"date":     "time.Time",
		"datetime": "time.Time",
		"decimal":  "float64",
	}
	if fieldType, ok := kFieldTypes[strings.ToLower(colDataType)]; !ok {
		return "string"
	} else {
		return fieldType
	}
}

func (m MysqlDriver) queryColumns(db *gmq.Db, dbName string, tables string, dbSchema DbSchema) error {
	objs := mysql.ColumnsObjs
	filter := objs.FilterTableSchema("=", dbName)
	if len(tables) > 0 {
		tableVs := strings.Split(tables, ",")
		filter = filter.And(objs.FilterTableName("IN", tableVs[0], tableVs[1:]...))
	}

	query := objs.Select().Where(filter).OrderBy("TableName", "OrdinalPosition")
	return query.Iterate(db, func(col mysql.Columns) bool {
		if _, ok := dbSchema[col.TableName]; !ok {
			dbSchema[col.TableName] = make(TableSchema, 0, 5)
		}
		sCol := Column{
			Schema:       col.TableSchema,
			TableName:    col.TableName,
			ColumnName:   col.ColumnName,
			DefaultValue: col.ColumnDefault,
			DataType:     m.dataType(col.DataType),
			ColumnType:   col.ColumnType,
			ColumnKey:    col.ColumnKey,
			Extra:        col.Extra,
			Comment:      col.ColumnComment,
		}
		dbSchema[col.TableName] = append(dbSchema[col.TableName], sCol)
		return true
	})
}

func (m MysqlDriver) useInformationSchema(dsn string, schema string) string {
	return strings.Replace(dsn, "/"+schema, "/information_schema", 1)
}
<commit_msg>fix binary blob type for mysql<commit_after>package drivers

import (
	"log"
	"strings"

	"github.com/mijia/modelq/drivers/mysql"
	"github.com/mijia/modelq/gmq"
)

type MysqlDriver struct{}

func (m MysqlDriver) LoadDatabaseSchema(dsnString, schema, tableNames string) (DbSchema, error) {
	log.Printf("[MySQL Driver] Start to load tables schema from db, %s, tables=%s", schema, tableNames)
	db, err := gmq.Open("mysql", m.useInformationSchema(dsnString, schema))
	if err != nil {
		return nil, err
	}
	defer db.Close()

	if err = db.Ping(); err != nil {
		return nil, err
	}

	dbSchema := make(DbSchema)
	if err = m.queryColumns(db, schema, tableNames, dbSchema); err != nil {
		return nil, err
	}
	log.Printf("[MySQL Driver] Loaded schema data of %d tables from db[%s]", len(dbSchema), schema)
	return dbSchema, nil
}

func (m MysqlDriver) dataType(colDataType string) string {
	kFieldTypes := map[string]string{
		"bigint":   "int64",
		"int":      "int",
		"tinyint":  "int",
		"char":     "string",
		"varchar":  "string",
		"blob":     "[]byte",
		"date":     "time.Time",
		"datetime": "time.Time",
		"decimal":  "float64",
	}
	if fieldType, ok := kFieldTypes[strings.ToLower(colDataType)]; !ok {
		return "string"
	} else {
		return fieldType
	}
}

func (m MysqlDriver) queryColumns(db *gmq.Db, dbName string, tables string, dbSchema DbSchema) error {
	objs := mysql.ColumnsObjs
	filter := objs.FilterTableSchema("=", dbName)
	if len(tables) > 0 {
		tableVs := strings.Split(tables, ",")
		filter = filter.And(objs.FilterTableName("IN", tableVs[0], tableVs[1:]...))
	}

	query := objs.Select().Where(filter).OrderBy("TableName", "OrdinalPosition")
	return query.Iterate(db, func(col mysql.Columns) bool {
		if _, ok := dbSchema[col.TableName]; !ok {
			dbSchema[col.TableName] = make(TableSchema, 0, 5)
		}
		sCol := Column{
			Schema:       col.TableSchema,
			TableName:    col.TableName,
			ColumnName:   col.ColumnName,
			DefaultValue: col.ColumnDefault,
			DataType:     m.dataType(col.DataType),
			ColumnType:   col.ColumnType,
			ColumnKey:    col.ColumnKey,
			Extra:        col.Extra,
			Comment:      col.ColumnComment,
		}
		dbSchema[col.TableName] = append(dbSchema[col.TableName], sCol)
		return true
	})
}

func (m MysqlDriver) useInformationSchema(dsn string, schema string) string {
	return strings.Replace(dsn, "/"+schema, "/information_schema", 1)
}
<|endoftext|>


<commit_before>package vmwpatch

const VERSION = "4.1.3"
const COPYRIGHT = "© 2014-2022 David Parsons"
<commit_msg>Development: Update version number<commit_after>package vmwpatch

const VERSION = "4.2.0"
const COPYRIGHT = "© 2014-2022 David Parsons"
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, 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 e2e

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/url"
	"os"
	"strings"
	"testing"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/gexpect"
	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/testutil"
)

const (
	etcdProcessBasePort = 20000
	certPath            = "../integration/fixtures/server.crt"
	privateKeyPath      = "../integration/fixtures/server.key.insecure"
	caPath              = "../integration/fixtures/ca.crt"
)

var (
	configNoTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  false,
		isPeerTLS:    false,
		initialToken: "new",
	}
	configTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  true,
		isPeerTLS:    true,
		initialToken: "new",
	}
	configClientTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  true,
		isPeerTLS:    false,
		initialToken: "new",
	}
	configPeerTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  false,
		isPeerTLS:    true,
		initialToken: "new",
	}
	configWithProxy = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    1,
		isClientTLS:  false,
		isPeerTLS:    false,
		initialToken: "new",
	}
	configWithProxyTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    1,
		isClientTLS:  true,
		isPeerTLS:    true,
		initialToken: "new",
	}
	configWithProxyPeerTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    1,
		isClientTLS:  false,
		isPeerTLS:    true,
		initialToken: "new",
	}
)

func configStandalone(cfg etcdProcessClusterConfig) *etcdProcessClusterConfig {
	ret := cfg
	ret.clusterSize = 1
	return &ret
}

func TestBasicOpsNoTLS(t *testing.T)        { testBasicOpsPutGet(t, &configNoTLS) }
func TestBasicOpsAllTLS(t *testing.T)       { testBasicOpsPutGet(t, &configTLS) }
func TestBasicOpsPeerTLS(t *testing.T)      { testBasicOpsPutGet(t, &configPeerTLS) }
func TestBasicOpsClientTLS(t *testing.T)    { testBasicOpsPutGet(t, &configClientTLS) }
func TestBasicOpsProxyNoTLS(t *testing.T)   { testBasicOpsPutGet(t, &configWithProxy) }
func TestBasicOpsProxyTLS(t *testing.T)     { testBasicOpsPutGet(t, &configWithProxyTLS) }
func TestBasicOpsProxyPeerTLS(t *testing.T) { testBasicOpsPutGet(t, &configWithProxyPeerTLS) }

func testBasicOpsPutGet(t *testing.T, cfg *etcdProcessClusterConfig) {
	defer testutil.AfterTest(t)

	// test doesn't use quorum gets, so ensure there are no followers to avoid
	// stale reads that will break the test
	cfg = configStandalone(*cfg)

	epc, err := newEtcdProcessCluster(cfg)
	if err != nil {
		t.Fatalf("could not start etcd process cluster (%v)", err)
	}
	defer func() {
		if err := epc.Close(); err != nil {
			t.Fatalf("error closing etcd processes (%v)", err)
		}
	}()

	expectPut := `{"action":"set","node":{"key":"/testKey","value":"foo","`
	if err := cURLPut(epc, "testKey", "foo", expectPut); err != nil {
		t.Fatalf("failed put with curl (%v)", err)
	}

	expectGet := `{"action":"get","node":{"key":"/testKey","value":"foo","`
	if err := cURLGet(epc, "testKey", expectGet); err != nil {
		t.Fatalf("failed get with curl (%v)", err)
	}
}

// cURLPrefixArgs builds the beginning of a curl command for a given key
// addressed to a random URL in the given cluster.
func cURLPrefixArgs(clus *etcdProcessCluster, key string) []string {
	cmdArgs := []string{"curl"}
	if clus.cfg.isClientTLS {
		cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath)
	}
	acurl := clus.procs[rand.Intn(clus.cfg.clusterSize)].cfg.acurl
	keyURL := acurl.String() + "/v2/keys/testKey"
	cmdArgs = append(cmdArgs, "-L", keyURL)
	return cmdArgs
}

func cURLPut(clus *etcdProcessCluster, key, val, expected string) error {
	args := append(cURLPrefixArgs(clus, key), "-XPUT", "-d", "value="+val)
	return spawnWithExpectedString(args, expected)
}

func cURLGet(clus *etcdProcessCluster, key, expected string) error {
	return spawnWithExpectedString(cURLPrefixArgs(clus, key), expected)
}

type etcdProcessCluster struct {
	cfg   *etcdProcessClusterConfig
	procs []*etcdProcess
}

type etcdProcess struct {
	cfg   *etcdProcessConfig
	proc  *gexpect.ExpectSubprocess
	donec chan struct{} // closed when Interact() terminates
}

type etcdProcessConfig struct {
	args        []string
	dataDirPath string
	acurl       url.URL
	isProxy     bool
}

type etcdProcessClusterConfig struct {
	clusterSize  int
	proxySize    int
	isClientTLS  bool
	isPeerTLS    bool
	initialToken string
}

// newEtcdProcessCluster launches a new cluster from etcd processes, returning
// a new etcdProcessCluster once all nodes are ready to accept client requests.
func newEtcdProcessCluster(cfg *etcdProcessClusterConfig) (*etcdProcessCluster, error) {
	etcdCfgs := cfg.etcdProcessConfigs()
	epc := &etcdProcessCluster{
		cfg:   cfg,
		procs: make([]*etcdProcess, cfg.clusterSize+cfg.proxySize),
	}

	// launch etcd processes
	for i := range etcdCfgs {
		proc, err := newEtcdProcess(etcdCfgs[i])
		if err != nil {
			epc.Close()
			return nil, err
		}
		epc.procs[i] = proc
	}

	// wait for cluster to start
	readyC := make(chan error, cfg.clusterSize+cfg.proxySize)
	readyStr := "etcdserver: set the initial cluster version to"
	for i := range etcdCfgs {
		go func(etcdp *etcdProcess) {
			rs := readyStr
			if etcdp.cfg.isProxy {
				// rs = "proxy: listening for client requests on"
				rs = "proxy: endpoints found"
			}
			ok, err := etcdp.proc.ExpectRegex(rs)
			if err != nil {
				readyC <- err
			} else if !ok {
				readyC <- fmt.Errorf("couldn't get expected output: '%s'", rs)
			} else {
				readyC <- nil
			}
			etcdp.proc.ReadLine()
			etcdp.proc.Interact() // this blocks(leaks) if another goroutine is reading
			etcdp.proc.ReadLine() // wait for leaky goroutine to accept an EOF
			close(etcdp.donec)
		}(epc.procs[i])
	}
	for range etcdCfgs {
		if err := <-readyC; err != nil {
			epc.Close()
			return nil, err
		}
	}
	return epc, nil
}

func newEtcdProcess(cfg *etcdProcessConfig) (*etcdProcess, error) {
	if fileutil.Exist("../bin/etcd") == false {
		return nil, fmt.Errorf("could not find etcd binary")
	}
	if err := os.RemoveAll(cfg.dataDirPath); err != nil {
		return nil, err
	}
	child, err := spawnCmd(append([]string{"../bin/etcd"}, cfg.args...))
	if err != nil {
		return nil, err
	}
	return &etcdProcess{cfg: cfg, proc: child, donec: make(chan struct{})}, nil
}

func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig {
	clientScheme := "http"
	if cfg.isClientTLS {
		clientScheme = "https"
	}
	peerScheme := "http"
	if cfg.isPeerTLS {
		peerScheme = "https"
	}

	etcdCfgs := make([]*etcdProcessConfig, cfg.clusterSize+cfg.proxySize)
	initialCluster := make([]string, cfg.clusterSize)
	for i := 0; i < cfg.clusterSize; i++ {
		port := etcdProcessBasePort + 2*i
		curl := url.URL{Scheme: clientScheme, Host: fmt.Sprintf("localhost:%d", port)}
		purl := url.URL{Scheme: peerScheme, Host: fmt.Sprintf("localhost:%d", port+1)}
		name := fmt.Sprintf("testname%d", i)
		dataDirPath, derr := ioutil.TempDir("", name+".etcd")
		if derr != nil {
			panic("could not get tempdir for datadir")
		}
		initialCluster[i] = fmt.Sprintf("%s=%s", name, purl.String())

		args := []string{
			"--name", name,
			"--listen-client-urls", curl.String(),
			"--advertise-client-urls", curl.String(),
			"--listen-peer-urls", purl.String(),
			"--initial-advertise-peer-urls", purl.String(),
			"--initial-cluster-token", cfg.initialToken,
			"--data-dir", dataDirPath,
		}

		args = append(args, cfg.tlsArgs()...)

		etcdCfgs[i] = &etcdProcessConfig{
			args:        args,
			dataDirPath: dataDirPath,
			acurl:       curl,
		}
	}
	for i := 0; i < cfg.proxySize; i++ {
		port := etcdProcessBasePort + 2*cfg.clusterSize + i + 1
		curl := url.URL{Scheme: clientScheme, Host: fmt.Sprintf("localhost:%d", port)}
		name := fmt.Sprintf("testname-proxy%d", i)
		dataDirPath, derr := ioutil.TempDir("", name+".etcd")
		if derr != nil {
			panic("could not get tempdir for datadir")
		}
		args := []string{
			"--name", name,
			"--proxy", "on",
			"--listen-client-urls", curl.String(),
			"--data-dir", dataDirPath,
		}
		args = append(args, cfg.tlsArgs()...)
		etcdCfgs[cfg.clusterSize+i] = &etcdProcessConfig{
			args:        args,
			dataDirPath: dataDirPath,
			acurl:       curl,
			isProxy:     true,
		}
	}

	initialClusterArgs := []string{"--initial-cluster", strings.Join(initialCluster, ",")}
	for i := range etcdCfgs {
		etcdCfgs[i].args = append(etcdCfgs[i].args, initialClusterArgs...)
	}

	return etcdCfgs
}

func (cfg *etcdProcessClusterConfig) tlsArgs() (args []string) {
	if cfg.isClientTLS {
		tlsClientArgs := []string{
			"--cert-file", certPath,
			"--key-file", privateKeyPath,
			"--ca-file", caPath,
		}
		args = append(args, tlsClientArgs...)
	}
	if cfg.isPeerTLS {
		tlsPeerArgs := []string{
			"--peer-cert-file", certPath,
			"--peer-key-file", privateKeyPath,
			"--peer-ca-file", caPath,
		}
		args = append(args, tlsPeerArgs...)
	}
	return args
}

func (epc *etcdProcessCluster) Close() (err error) {
	for _, p := range epc.procs {
		if p == nil {
			continue
		}
		os.RemoveAll(p.cfg.dataDirPath)
		if curErr := p.proc.Close(); curErr != nil {
			if err != nil {
				err = fmt.Errorf("%v; %v", err, curErr)
			} else {
				err = curErr
			}
		}
		<-p.donec
	}
	return err
}

func spawnCmd(args []string) (*gexpect.ExpectSubprocess, error) {
	// redirect stderr to stdout since gexpect only uses stdout
	cmd := `/bin/sh -c "` + strings.Join(args, " ") + ` 2>&1 "`
	return gexpect.Spawn(cmd)
}

func spawnWithExpect(args []string, expected string) error {
	proc, err := spawnCmd(args)
	if err != nil {
		return err
	}
	ok, err := proc.ExpectRegex(expected)
	perr := proc.Close()
	if err != nil {
		return err
	}
	if !ok {
		return fmt.Errorf("couldn't get expected output: '%s'", expected)
	}
	return perr
}

// spawnWithExpectedString compares outputs in string format.
// This is useful when gexpect does not match regex correctly with
// some UTF-8 format characters.
func spawnWithExpectedString(args []string, expected string) error {
	proc, err := spawnCmd(args)
	if err != nil {
		return err
	}
	s, err := proc.ReadLine()
	perr := proc.Close()
	if err != nil {
		return err
	}
	if !strings.Contains(s, expected) {
		return fmt.Errorf("expected %q, got %q", expected, s)
	}
	return perr
}

// proxies returns only the proxy etcdProcess.
func (epc *etcdProcessCluster) proxies() []*etcdProcess {
	return epc.procs[epc.cfg.clusterSize:]
}

func (epc *etcdProcessCluster) backends() []*etcdProcess {
	return epc.procs[:epc.cfg.clusterSize]
}
<commit_msg>e2e: test auto tls<commit_after>// Copyright 2016 CoreOS, 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 e2e

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/url"
	"os"
	"strings"
	"testing"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/gexpect"
	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/testutil"
)

const (
	etcdProcessBasePort = 20000
	certPath            = "../integration/fixtures/server.crt"
	privateKeyPath      = "../integration/fixtures/server.key.insecure"
	caPath              = "../integration/fixtures/ca.crt"
)

var (
	configNoTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  false,
		isPeerTLS:    false,
		initialToken: "new",
	}
	configAutoTLS = etcdProcessClusterConfig{
		clusterSize:   3,
		isPeerTLS:     true,
		isPeerAutoTLS: true,
		initialToken:  "new",
	}
	configTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  true,
		isPeerTLS:    true,
		initialToken: "new",
	}
	configClientTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  true,
		isPeerTLS:    false,
		initialToken: "new",
	}
	configPeerTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    0,
		isClientTLS:  false,
		isPeerTLS:    true,
		initialToken: "new",
	}
	configWithProxy = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    1,
		isClientTLS:  false,
		isPeerTLS:    false,
		initialToken: "new",
	}
	configWithProxyTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    1,
		isClientTLS:  true,
		isPeerTLS:    true,
		initialToken: "new",
	}
	configWithProxyPeerTLS = etcdProcessClusterConfig{
		clusterSize:  3,
		proxySize:    1,
		isClientTLS:  false,
		isPeerTLS:    true,
		initialToken: "new",
	}
)

func configStandalone(cfg etcdProcessClusterConfig) *etcdProcessClusterConfig {
	ret := cfg
	ret.clusterSize = 1
	return &ret
}

func TestBasicOpsNoTLS(t *testing.T)        { testBasicOpsPutGet(t, &configNoTLS) }
func TestBasicOpsAutoTLS(t *testing.T)      { testBasicOpsPutGet(t, &configAutoTLS) }
func TestBasicOpsAllTLS(t *testing.T)       { testBasicOpsPutGet(t, &configTLS) }
func TestBasicOpsPeerTLS(t *testing.T)      { testBasicOpsPutGet(t, &configPeerTLS) }
func TestBasicOpsClientTLS(t *testing.T)    { testBasicOpsPutGet(t, &configClientTLS) }
func TestBasicOpsProxyNoTLS(t *testing.T)   { testBasicOpsPutGet(t, &configWithProxy) }
func TestBasicOpsProxyTLS(t *testing.T)     { testBasicOpsPutGet(t, &configWithProxyTLS) }
func TestBasicOpsProxyPeerTLS(t *testing.T) { testBasicOpsPutGet(t, &configWithProxyPeerTLS) }

func testBasicOpsPutGet(t *testing.T, cfg *etcdProcessClusterConfig) {
	defer testutil.AfterTest(t)

	// test doesn't use quorum gets, so ensure there are no followers to avoid
	// stale reads that will break the test
	cfg = configStandalone(*cfg)

	epc, err := newEtcdProcessCluster(cfg)
	if err != nil {
		t.Fatalf("could not start etcd process cluster (%v)", err)
	}
	defer func() {
		if err := epc.Close(); err != nil {
			t.Fatalf("error closing etcd processes (%v)", err)
		}
	}()

	expectPut := `{"action":"set","node":{"key":"/testKey","value":"foo","`
	if err := cURLPut(epc, "testKey", "foo", expectPut); err != nil {
		t.Fatalf("failed put with curl (%v)", err)
	}

	expectGet := `{"action":"get","node":{"key":"/testKey","value":"foo","`
	if err := cURLGet(epc, "testKey", expectGet); err != nil {
		t.Fatalf("failed get with curl (%v)", err)
	}
}

// cURLPrefixArgs builds the beginning of a curl command for a given key
// addressed to a random URL in the given cluster.
func cURLPrefixArgs(clus *etcdProcessCluster, key string) []string {
	cmdArgs := []string{"curl"}
	if clus.cfg.isClientTLS {
		cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath)
	}
	acurl := clus.procs[rand.Intn(clus.cfg.clusterSize)].cfg.acurl
	keyURL := acurl.String() + "/v2/keys/testKey"
	cmdArgs = append(cmdArgs, "-L", keyURL)
	return cmdArgs
}

func cURLPut(clus *etcdProcessCluster, key, val, expected string) error {
	args := append(cURLPrefixArgs(clus, key), "-XPUT", "-d", "value="+val)
	return spawnWithExpectedString(args, expected)
}

func cURLGet(clus *etcdProcessCluster, key, expected string) error {
	return spawnWithExpectedString(cURLPrefixArgs(clus, key), expected)
}

type etcdProcessCluster struct {
	cfg   *etcdProcessClusterConfig
	procs []*etcdProcess
}

type etcdProcess struct {
	cfg   *etcdProcessConfig
	proc  *gexpect.ExpectSubprocess
	donec chan struct{} // closed when Interact() terminates
}

type etcdProcessConfig struct {
	args        []string
	dataDirPath string
	acurl       url.URL
	isProxy     bool
}

type etcdProcessClusterConfig struct {
	clusterSize   int
	proxySize     int
	isClientTLS   bool
	isPeerTLS     bool
	isPeerAutoTLS bool
	initialToken  string
}

// newEtcdProcessCluster launches a new cluster from etcd processes, returning
// a new etcdProcessCluster once all nodes are ready to accept client requests.
func newEtcdProcessCluster(cfg *etcdProcessClusterConfig) (*etcdProcessCluster, error) {
	etcdCfgs := cfg.etcdProcessConfigs()
	epc := &etcdProcessCluster{
		cfg:   cfg,
		procs: make([]*etcdProcess, cfg.clusterSize+cfg.proxySize),
	}

	// launch etcd processes
	for i := range etcdCfgs {
		proc, err := newEtcdProcess(etcdCfgs[i])
		if err != nil {
			epc.Close()
			return nil, err
		}
		epc.procs[i] = proc
	}

	// wait for cluster to start
	readyC := make(chan error, cfg.clusterSize+cfg.proxySize)
	readyStr := "etcdserver: set the initial cluster version to"
	for i := range etcdCfgs {
		go func(etcdp *etcdProcess) {
			rs := readyStr
			if etcdp.cfg.isProxy {
				// rs = "proxy: listening for client requests on"
				rs = "proxy: endpoints found"
			}
			ok, err := etcdp.proc.ExpectRegex(rs)
			if err != nil {
				readyC <- err
			} else if !ok {
				readyC <- fmt.Errorf("couldn't get expected output: '%s'", rs)
			} else {
				readyC <- nil
			}
			etcdp.proc.ReadLine()
			etcdp.proc.Interact() // this blocks(leaks) if another goroutine is reading
			etcdp.proc.ReadLine() // wait for leaky goroutine to accept an EOF
			close(etcdp.donec)
		}(epc.procs[i])
	}
	for range etcdCfgs {
		if err := <-readyC; err != nil {
			epc.Close()
			return nil, err
		}
	}
	return epc, nil
}

func newEtcdProcess(cfg *etcdProcessConfig) (*etcdProcess, error) {
	if fileutil.Exist("../bin/etcd") == false {
		return nil, fmt.Errorf("could not find etcd binary")
	}
	if err := os.RemoveAll(cfg.dataDirPath); err != nil {
		return nil, err
	}
	child, err := spawnCmd(append([]string{"../bin/etcd"}, cfg.args...))
	if err != nil {
		return nil, err
	}
	return &etcdProcess{cfg: cfg, proc: child, donec: make(chan struct{})}, nil
}

func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig {
	clientScheme := "http"
	if cfg.isClientTLS {
		clientScheme = "https"
	}
	peerScheme := "http"
	if cfg.isPeerTLS {
		peerScheme = "https"
	}

	etcdCfgs := make([]*etcdProcessConfig, cfg.clusterSize+cfg.proxySize)
	initialCluster := make([]string, cfg.clusterSize)
	for i := 0; i < cfg.clusterSize; i++ {
		port := etcdProcessBasePort + 2*i
		curl := url.URL{Scheme: clientScheme, Host: fmt.Sprintf("localhost:%d", port)}
		purl := url.URL{Scheme: peerScheme, Host: fmt.Sprintf("localhost:%d", port+1)}
		name := fmt.Sprintf("testname%d", i)
		dataDirPath, derr := ioutil.TempDir("", name+".etcd")
		if derr != nil {
			panic("could not get tempdir for datadir")
		}
		initialCluster[i] = fmt.Sprintf("%s=%s", name, purl.String())

		args := []string{
			"--name", name,
			"--listen-client-urls", curl.String(),
			"--advertise-client-urls", curl.String(),
			"--listen-peer-urls", purl.String(),
			"--initial-advertise-peer-urls", purl.String(),
			"--initial-cluster-token", cfg.initialToken,
			"--data-dir", dataDirPath,
		}

		args = append(args, cfg.tlsArgs()...)

		etcdCfgs[i] = &etcdProcessConfig{
			args:        args,
			dataDirPath: dataDirPath,
			acurl:       curl,
		}
	}
	for i := 0; i < cfg.proxySize; i++ {
		port := etcdProcessBasePort + 2*cfg.clusterSize + i + 1
		curl := url.URL{Scheme: clientScheme, Host: fmt.Sprintf("localhost:%d", port)}
		name := fmt.Sprintf("testname-proxy%d", i)
		dataDirPath, derr := ioutil.TempDir("", name+".etcd")
		if derr != nil {
			panic("could not get tempdir for datadir")
		}
		args := []string{
			"--name", name,
			"--proxy", "on",
			"--listen-client-urls", curl.String(),
			"--data-dir", dataDirPath,
		}
		args = append(args, cfg.tlsArgs()...)
		etcdCfgs[cfg.clusterSize+i] = &etcdProcessConfig{
			args:        args,
			dataDirPath: dataDirPath,
			acurl:       curl,
			isProxy:     true,
		}
	}

	initialClusterArgs := []string{"--initial-cluster", strings.Join(initialCluster, ",")}
	for i := range etcdCfgs {
		etcdCfgs[i].args = append(etcdCfgs[i].args, initialClusterArgs...)
	}

	return etcdCfgs
}

func (cfg *etcdProcessClusterConfig) tlsArgs() (args []string) {
	if cfg.isClientTLS {
		tlsClientArgs := []string{
			"--cert-file", certPath,
			"--key-file", privateKeyPath,
			"--ca-file", caPath,
		}
		args = append(args, tlsClientArgs...)
	}
	if cfg.isPeerTLS {
		if cfg.isPeerAutoTLS {
			args = append(args, "--peer-auto-tls=true")
		} else {
			tlsPeerArgs := []string{
				"--peer-cert-file", certPath,
				"--peer-key-file", privateKeyPath,
				"--peer-ca-file", caPath,
			}
			args = append(args, tlsPeerArgs...)
		}
	}
	return args
}

func (epc *etcdProcessCluster) Close() (err error) {
	for _, p := range epc.procs {
		if p == nil {
			continue
		}
		os.RemoveAll(p.cfg.dataDirPath)
		if curErr := p.proc.Close(); curErr != nil {
			if err != nil {
				err = fmt.Errorf("%v; %v", err, curErr)
			} else {
				err = curErr
			}
		}
		<-p.donec
	}
	return err
}

func spawnCmd(args []string) (*gexpect.ExpectSubprocess, error) {
	// redirect stderr to stdout since gexpect only uses stdout
	cmd := `/bin/sh -c "` + strings.Join(args, " ") + ` 2>&1 "`
	return gexpect.Spawn(cmd)
}

func spawnWithExpect(args []string, expected string) error {
	proc, err := spawnCmd(args)
	if err != nil {
		return err
	}
	ok, err := proc.ExpectRegex(expected)
	perr := proc.Close()
	if err != nil {
		return err
	}
	if !ok {
		return fmt.Errorf("couldn't get expected output: '%s'", expected)
	}
	return perr
}

// spawnWithExpectedString compares outputs in string format.
// This is useful when gexpect does not match regex correctly with
// some UTF-8 format characters.
func spawnWithExpectedString(args []string, expected string) error {
	proc, err := spawnCmd(args)
	if err != nil {
		return err
	}
	s, err := proc.ReadLine()
	perr := proc.Close()
	if err != nil {
		return err
	}
	if !strings.Contains(s, expected) {
		return fmt.Errorf("expected %q, got %q", expected, s)
	}
	return perr
}

// proxies returns only the proxy etcdProcess.
func (epc *etcdProcessCluster) proxies() []*etcdProcess {
	return epc.procs[epc.cfg.clusterSize:]
}

func (epc *etcdProcessCluster) backends() []*etcdProcess {
	return epc.procs[:epc.cfg.clusterSize]
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// 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 ui

import "github.com/go-gl/glfw/v3.1/glfw"

type focusable interface {
	Drawable
	setFocused(bool)
	handleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey)
	handleChar(w *glfw.Window, char rune)
}

func HandleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if key == glfw.KeyTab && action == glfw.Release {
		CycleFocus()
		return
	}
	if f := getFocused(); f != nil {
		f.handleKey(w, key, scancode, action, mods)
	}
}

func HandleChar(w *glfw.Window, char rune) {
	if f := getFocused(); f != nil {
		f.handleChar(w, char)
	}
}

var focused focusable

func getFocused() focusable {
	if focused != nil {
		for _, d := range drawables {
			if d.Drawable == focused {
				return d.Drawable.(focusable)
			}
		}
	}
	for _, d := range drawables {
		if f, ok := d.Drawable.(focusable); ok {
			focus(f)
			return focused
		}
	}
	return nil
}

func CycleFocus() {
	pos := 0
	l := len(drawables)
	for i, d := range drawables {
		if d.Drawable == focused {
			pos = (i + 1) % l
			break
		}
	}
	max := l
	for ; max >= 0; max-- {
		dr := drawables[pos]
		if f, ok := dr.Drawable.(focusable); ok {
			focus(f)
			break
		}
		pos = (pos + 1) % l
	}
}

func focus(f focusable) {
	if focused != nil {
		focused.setFocused(false)
	}
	focused = f
	focused.setFocused(true)
}
<commit_msg>ui: add some comments<commit_after>// Copyright 2015 Matthew Collins
//
// 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 ui

import "github.com/go-gl/glfw/v3.1/glfw"

// focusable is a drawable that can be focsued for keyboard input
type focusable interface {
	Drawable
	setFocused(bool)
	handleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey)
	handleChar(w *glfw.Window, char rune)
}

// HandleKey passes the input to the focused drawable
func HandleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if key == glfw.KeyTab && action == glfw.Release {
		CycleFocus()
		return
	}
	if f := getFocused(); f != nil {
		f.handleKey(w, key, scancode, action, mods)
	}
}

// HandleChar passes the input to the focused drawable
func HandleChar(w *glfw.Window, char rune) {
	if f := getFocused(); f != nil {
		f.handleChar(w, char)
	}
}

// Currently focused drawable
var focused focusable

// Returns the currently focused drawable or nil
// Tries to focus an drawable if one isn't currently focused
func getFocused() focusable {
	if focused != nil {
		// Ensure the drawable is focused
		for _, d := range drawables {
			if d.Drawable == focused {
				return d.Drawable.(focusable)
			}
		}
	}
	// Try to focus another drawable
	for _, d := range drawables {
		if f, ok := d.Drawable.(focusable); ok {
			focus(f)
			return focused
		}
	}
	// Clear the focus if one isn't found incase this
	// fell through from the first check
	focus(nil)
	return nil
}

// CycleFocus changes the focus to the next drawable
func CycleFocus() {
	pos := 0
	l := len(drawables)
	// Find our drawable
	for i, d := range drawables {
		if d.Drawable == focused {
			pos = (i + 1) % l
			break
		}
	}
	// Make sure we don't loop forever on scenes without focusable drawables
	max := l
	for ; max >= 0; max-- {
		dr := drawables[pos]
		if f, ok := dr.Drawable.(focusable); ok {
			focus(f)
			break
		}
		pos = (pos + 1) % l
	}
}

// Changes the focused drawable
func focus(f focusable) {
	if focused != nil {
		focused.setFocused(false)
	}
	focused = f
	if focused != nil {
		focused.setFocused(true)
	}
}
<|endoftext|>


<commit_before>package unit

import (
	"sort"
	"strconv"
)

// Uniter is an interface representing a type that can be converted
// to a unit.
type Uniter interface {
	Unit() *Unit
}

// Dimension is a type representing an SI base dimension or other
// orthogonal dimension. If a new dimension is desired for a
// domain-specific problem, NewDimension should be used. Integers
// should never be cast as type dimension
//	// Good: Create a package constant with an init function
//	var MyDimension unit.Dimension
//	init(){
//		MyDimension = NewDimension("my")
//	}
//	main(){
//		var := MyDimension(28.2)
//	}
type Dimension int

const (
	// SI Base Units
	reserved Dimension = iota
	CurrentDim
	LengthDim
	LuminousIntensityDim
	MassDim
	TemperatureDim
	TimeDim
	// Other common SI Dimensions
	AngleDim // e.g. radians
)

var (
	symbols = []string{
		CurrentDim:           "A",
		LengthDim:            "m",
		LuminousIntensityDim: "cd",
		MassDim:              "kg",
		TemperatureDim:       "K",
		TimeDim:              "s",
		AngleDim:             "rad",
	}

	// for guaranteeing there aren't two identical symbols
	dimensions = map[string]Dimension{
		"A":   CurrentDim,
		"m":   LengthDim,
		"cd":  LuminousIntensityDim,
		"kg":  MassDim,
		"K":   TemperatureDim,
		"s":   TimeDim,
		"rad": AngleDim,

		// Reserve common SI symbols
		// base units
		"mol": reserved,
		// prefixes
		"Y":  reserved,
		"Z":  reserved,
		"E":  reserved,
		"P":  reserved,
		"T":  reserved,
		"G":  reserved,
		"M":  reserved,
		"k":  reserved,
		"h":  reserved,
		"da": reserved,
		"d":  reserved,
		"c":  reserved,
		"μ":  reserved,
		"n":  reserved,
		"p":  reserved,
		"f":  reserved,
		"a":  reserved,
		"z":  reserved,
		"y":  reserved,
		// SI Derived units with special symbols
		"sr":  reserved,
		"F":   reserved,
		"C":   reserved,
		"S":   reserved,
		"H":   reserved,
		"V":   reserved,
		"Ω":   reserved,
		"J":   reserved,
		"N":   reserved,
		"Hz":  reserved,
		"lx":  reserved,
		"lm":  reserved,
		"Wb":  reserved,
		"W":   reserved,
		"Pa":  reserved,
		"Bq":  reserved,
		"Gy":  reserved,
		"Sv":  reserved,
		"kat": reserved,
		// Units in use with SI
		"ha": reserved,
		"L":  reserved,
		"l":  reserved,
		// Units in Use Temporarily with SI
		"bar": reserved,
		"b":   reserved,
		"Ci":  reserved,
		"R":   reserved,
		"rd":  reserved,
		"rem": reserved,
	}
)

// TODO: Should we actually reserve "common" SI unit symbols ("N", "J", etc.) so there isn't confusion
// TODO: If we have a fancier ParseUnit, maybe the 'reserved' symbols should be a different map
// 		map[string]string which says how they go?

// Dimensions represent the dimensionality of the unit in powers
// of that dimension. If a key is not present, the power of that
// dimension is zero. Dimensions is used in conjuction with NewUnit
type Dimensions map[Dimension]int

// NewDimension returns a new dimension variable which will have a
// unique representation across packages to prevent accidental overlap.
// The input string represents a symbol name which will be used for printing
// Unit types. This symbol may not overlap with any of the SI base units
// or other symbols of common use in SI ("kg", "J", "μ", etc.). A list of
// such symbols can be found at http://lamar.colostate.edu/~hillger/basic.htm or
// by consulting the package source. Furthermore, the provided symbol is also
// forbidden from overlapping with other packages calling NewDimension. NewDimension
// is expecting to be used only during initialization, and as such it will panic
// if the symbol matching an existing symbol
// NewDimension should only be called for unit types that are actually orthogonal
// to the base dimensions defined in this package. Please see the package-level
// documentation for further explanation. Calls to NewDimension are not thread safe.
func NewDimension(symbol string) Dimension {
	_, ok := dimensions[symbol]
	if ok {
		panic("unit: dimension string " + symbol + " already used")
	}
	symbols = append(symbols, symbol)
	d := Dimension(len(symbols))
	dimensions[symbol] = d
	return d
}

// Unit is a type a value with generic SI units. Most useful for
// translating between dimensions, for example, by multiplying
// an acceleration with a mass to get a force. Please see the
// package documentation for further explanation.
type Unit struct {
	dimensions map[Dimension]int // Map for custom dimensions
	value      float64
}

// New creates a new variable of type Unit which has the value
// specified by value and the dimensions specified by the
// base units struct. The value is always in SI Units.
//
// Example: To create an acceleration of 3 m/s^2, one could do
// myvar := CreateUnit(3.0, &Dimensions{unit.LengthDim: 1, unit.TimeDim: -2})
func New(value float64, d Dimensions) *Unit {
	u := &Unit{
		dimensions: make(map[Dimension]int),
	}
	for key, val := range d {
		u.dimensions[key] = val
	}
	u.value = value
	return u
}

// DimensionsMatch checks if the dimensions of two Uniters are the same
func DimensionsMatch(a, b Uniter) bool {
	aUnit := a.Unit()
	bUnit := b.Unit()
	if len(aUnit.dimensions) != len(bUnit.dimensions) {
		return false
	}
	for key, val := range aUnit.dimensions {
		if bUnit.dimensions[key] != val {
			return false
		}
	}
	return true
}

// Add adds the function argument to the reciever. Panics if the units of
// the receiver and the argument don't match.
func (u *Unit) Add(uniter Uniter) *Unit {
	a := uniter.Unit()
	if !DimensionsMatch(u, a) {
		panic("unit: mismatched dimensions in addition")
	}
	u.value += a.value
	return u
}

// Unit implements the Uniter interface
func (u *Unit) Unit() *Unit {
	return u
}

// Mul multiply the receiver by the input changing the dimensions
// of the receiver as appropriate. The input is not changed
func (u *Unit) Mul(uniter Uniter) *Unit {
	a := uniter.Unit()
	for key, val := range a.dimensions {
		u.dimensions[key] += val
	}
	u.value *= a.value
	return u
}

// Div divides the receiver by the argument changing the
// dimensions of the receiver as appropriate
func (u *Unit) Div(uniter Uniter) *Unit {
	a := uniter.Unit()
	u.value /= a.value
	for key, val := range a.dimensions {
		u.dimensions[key] -= val
	}
	return u
}

// Value return the raw value of the unit as a float64. Use of this
// method is, in general, not recommended, though it can be useful
// for printing. Instead, the FromUnit type of a specific
// dimension should be used to guarantee dimension consistency
func (u *Unit) Value() float64 {
	return u.value
}

type symbolString struct {
	symbol string
	pow    int
}

type unitPrinters []symbolString

func (u unitPrinters) Len() int {
	return len(u)
}

func (u unitPrinters) Less(i, j int) bool {
	return u[i].symbol < u[j].symbol
}

func (u unitPrinters) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// String makes Unit satisfy the stringer interface. The unit is printed
// using strconv.FormatFloat(unit.value, 'e', -1, 64), with dimensions
// appended. If the power if the dimension is not zero or one,
// symbol^power is appended, if the power is one, just the symbol is appended
// and if the power is zero, nothing is appended. Dimensions are appended
// in order by symbol name.
func (u *Unit) String() string {
	str := strconv.FormatFloat(u.value, 'e', -1, 64)
	// Map iterates randomly, but print should be in a fixed order. Can't use
	// dimension number, because for user-defined dimension that number may
	// not be fixed from run to run.
	data := make(unitPrinters, 0, 10)
	for dimension, power := range u.dimensions {
		if power != 0 {
			data = append(data, symbolString{symbols[dimension], power})
		}
	}
	sort.Sort(data)
	for _, s := range data {
		str += " " + s.symbol
		if s.pow != 1 {
			str += "^" + strconv.Itoa(s.pow)
		}
	}
	return str
}
<commit_msg>Size slice more carefully<commit_after>package unit

import (
	"sort"
	"strconv"
)

// Uniter is an interface representing a type that can be converted
// to a unit.
type Uniter interface {
	Unit() *Unit
}

// Dimension is a type representing an SI base dimension or other
// orthogonal dimension. If a new dimension is desired for a
// domain-specific problem, NewDimension should be used. Integers
// should never be cast as type dimension
//	// Good: Create a package constant with an init function
//	var MyDimension unit.Dimension
//	init(){
//		MyDimension = NewDimension("my")
//	}
//	main(){
//		var := MyDimension(28.2)
//	}
type Dimension int

const (
	// SI Base Units
	reserved Dimension = iota
	CurrentDim
	LengthDim
	LuminousIntensityDim
	MassDim
	TemperatureDim
	TimeDim
	// Other common SI Dimensions
	AngleDim // e.g. radians
)

var (
	symbols = []string{
		CurrentDim:           "A",
		LengthDim:            "m",
		LuminousIntensityDim: "cd",
		MassDim:              "kg",
		TemperatureDim:       "K",
		TimeDim:              "s",
		AngleDim:             "rad",
	}

	// for guaranteeing there aren't two identical symbols
	dimensions = map[string]Dimension{
		"A":   CurrentDim,
		"m":   LengthDim,
		"cd":  LuminousIntensityDim,
		"kg":  MassDim,
		"K":   TemperatureDim,
		"s":   TimeDim,
		"rad": AngleDim,

		// Reserve common SI symbols
		// base units
		"mol": reserved,
		// prefixes
		"Y":  reserved,
		"Z":  reserved,
		"E":  reserved,
		"P":  reserved,
		"T":  reserved,
		"G":  reserved,
		"M":  reserved,
		"k":  reserved,
		"h":  reserved,
		"da": reserved,
		"d":  reserved,
		"c":  reserved,
		"μ":  reserved,
		"n":  reserved,
		"p":  reserved,
		"f":  reserved,
		"a":  reserved,
		"z":  reserved,
		"y":  reserved,
		// SI Derived units with special symbols
		"sr":  reserved,
		"F":   reserved,
		"C":   reserved,
		"S":   reserved,
		"H":   reserved,
		"V":   reserved,
		"Ω":   reserved,
		"J":   reserved,
		"N":   reserved,
		"Hz":  reserved,
		"lx":  reserved,
		"lm":  reserved,
		"Wb":  reserved,
		"W":   reserved,
		"Pa":  reserved,
		"Bq":  reserved,
		"Gy":  reserved,
		"Sv":  reserved,
		"kat": reserved,
		// Units in use with SI
		"ha": reserved,
		"L":  reserved,
		"l":  reserved,
		// Units in Use Temporarily with SI
		"bar": reserved,
		"b":   reserved,
		"Ci":  reserved,
		"R":   reserved,
		"rd":  reserved,
		"rem": reserved,
	}
)

// TODO: Should we actually reserve "common" SI unit symbols ("N", "J", etc.) so there isn't confusion
// TODO: If we have a fancier ParseUnit, maybe the 'reserved' symbols should be a different map
// 		map[string]string which says how they go?

// Dimensions represent the dimensionality of the unit in powers
// of that dimension. If a key is not present, the power of that
// dimension is zero. Dimensions is used in conjuction with NewUnit
type Dimensions map[Dimension]int

// NewDimension returns a new dimension variable which will have a
// unique representation across packages to prevent accidental overlap.
// The input string represents a symbol name which will be used for printing
// Unit types. This symbol may not overlap with any of the SI base units
// or other symbols of common use in SI ("kg", "J", "μ", etc.). A list of
// such symbols can be found at http://lamar.colostate.edu/~hillger/basic.htm or
// by consulting the package source. Furthermore, the provided symbol is also
// forbidden from overlapping with other packages calling NewDimension. NewDimension
// is expecting to be used only during initialization, and as such it will panic
// if the symbol matching an existing symbol
// NewDimension should only be called for unit types that are actually orthogonal
// to the base dimensions defined in this package. Please see the package-level
// documentation for further explanation. Calls to NewDimension are not thread safe.
func NewDimension(symbol string) Dimension {
	_, ok := dimensions[symbol]
	if ok {
		panic("unit: dimension string " + symbol + " already used")
	}
	symbols = append(symbols, symbol)
	d := Dimension(len(symbols))
	dimensions[symbol] = d
	return d
}

// Unit is a type a value with generic SI units. Most useful for
// translating between dimensions, for example, by multiplying
// an acceleration with a mass to get a force. Please see the
// package documentation for further explanation.
type Unit struct {
	dimensions map[Dimension]int // Map for custom dimensions
	value      float64
}

// New creates a new variable of type Unit which has the value
// specified by value and the dimensions specified by the
// base units struct. The value is always in SI Units.
//
// Example: To create an acceleration of 3 m/s^2, one could do
// myvar := CreateUnit(3.0, &Dimensions{unit.LengthDim: 1, unit.TimeDim: -2})
func New(value float64, d Dimensions) *Unit {
	u := &Unit{
		dimensions: make(map[Dimension]int),
	}
	for key, val := range d {
		u.dimensions[key] = val
	}
	u.value = value
	return u
}

// DimensionsMatch checks if the dimensions of two Uniters are the same
func DimensionsMatch(a, b Uniter) bool {
	aUnit := a.Unit()
	bUnit := b.Unit()
	if len(aUnit.dimensions) != len(bUnit.dimensions) {
		return false
	}
	for key, val := range aUnit.dimensions {
		if bUnit.dimensions[key] != val {
			return false
		}
	}
	return true
}

// Add adds the function argument to the reciever. Panics if the units of
// the receiver and the argument don't match.
func (u *Unit) Add(uniter Uniter) *Unit {
	a := uniter.Unit()
	if !DimensionsMatch(u, a) {
		panic("unit: mismatched dimensions in addition")
	}
	u.value += a.value
	return u
}

// Unit implements the Uniter interface
func (u *Unit) Unit() *Unit {
	return u
}

// Mul multiply the receiver by the input changing the dimensions
// of the receiver as appropriate. The input is not changed
func (u *Unit) Mul(uniter Uniter) *Unit {
	a := uniter.Unit()
	for key, val := range a.dimensions {
		u.dimensions[key] += val
	}
	u.value *= a.value
	return u
}

// Div divides the receiver by the argument changing the
// dimensions of the receiver as appropriate
func (u *Unit) Div(uniter Uniter) *Unit {
	a := uniter.Unit()
	u.value /= a.value
	for key, val := range a.dimensions {
		u.dimensions[key] -= val
	}
	return u
}

// Value return the raw value of the unit as a float64. Use of this
// method is, in general, not recommended, though it can be useful
// for printing. Instead, the FromUnit type of a specific
// dimension should be used to guarantee dimension consistency
func (u *Unit) Value() float64 {
	return u.value
}

type symbolString struct {
	symbol string
	pow    int
}

type unitPrinters []symbolString

func (u unitPrinters) Len() int {
	return len(u)
}

func (u unitPrinters) Less(i, j int) bool {
	return u[i].symbol < u[j].symbol
}

func (u unitPrinters) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// String makes Unit satisfy the stringer interface. The unit is printed
// using strconv.FormatFloat(unit.value, 'e', -1, 64), with dimensions
// appended. If the power if the dimension is not zero or one,
// symbol^power is appended, if the power is one, just the symbol is appended
// and if the power is zero, nothing is appended. Dimensions are appended
// in order by symbol name.
func (u *Unit) String() string {
	str := strconv.FormatFloat(u.value, 'e', -1, 64)
	// Map iterates randomly, but print should be in a fixed order. Can't use
	// dimension number, because for user-defined dimension that number may
	// not be fixed from run to run.
	data := make(unitPrinters, 0, len(u.dimensions))
	for dimension, power := range u.dimensions {
		if power != 0 {
			data = append(data, symbolString{symbols[dimension], power})
		}
	}
	sort.Sort(data)
	for _, s := range data {
		str += " " + s.symbol
		if s.pow != 1 {
			str += "^" + strconv.Itoa(s.pow)
		}
	}
	return str
}
<|endoftext|>


<commit_before>package edgegrid

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"testing"
)

var (
	testFile  = "testdata.json"
	timestamp = "20140321T19:34:21+0000"
	nonce     = "nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
	config    = Config{
		Host:         "https://akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net/",
		AccessToken:  "akab-access-token-xxx-xxxxxxxxxxxxxxxx",
		ClientToken:  "akab-client-token-xxx-xxxxxxxxxxxxxxxx",
		ClientSecret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=",
		MaxBody:      2048,
		Debug:        true,
		HeaderToSign: []string{
			"X-Test1",
			"X-Test2",
			"X-Test3",
		},
	}
)

type JsonTests struct {
	Tests []Test `json:"tests"`
}
type Test struct {
	Name    string `json:"testName"`
	Request struct {
		Method  string              `json:"method"`
		Path    string              `json:"path"`
		Headers []map[string]string `json:"headers"`
		Data    string              `json:"data"`
	} `json:"request"`
	ExpectedAuthorization string `json:"expectedAuthorization"`
}

func TestMakeEdgeTimeStamp(t *testing.T) {
	actual := makeEdgeTimeStamp()
	expected := regexp.MustCompile(`^\d{4}[0-1][0-9][0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\+0000$`)
	if assert.Regexp(t, expected, actual, "Fail: Regex do not match") {
		t.Log("Pass: Regex matches")
	}
}

func TestCreateNonce(t *testing.T) {
	actual := createNonce()
	for i := 0; i < 100; i++ {
		expected := createNonce()
		assert.NotEqual(t, actual, expected, "Fail: Nonce matches")
	}
}

func TestCreateAuthHeader(t *testing.T) {
	var edgegrid JsonTests
	byt, err := ioutil.ReadFile(testFile)
	if err != nil {
		t.Fatalf("Test file not found, err %s", err)
	}
	url, err := url.Parse(config.Host)
	if err != nil {
		t.Fatalf("URL is not parsable, err %s", err)
	}
	json.Unmarshal(byt, &edgegrid)
	for _, edge := range edgegrid.Tests {
		url.Path = edge.Request.Path
		req, _ := http.NewRequest(
			edge.Request.Method,
			url.String(),
			bytes.NewBuffer([]byte(edge.Request.Data)),
		)
		for _, header := range edge.Request.Headers {
			for k, v := range header {
				req.Header.Set(k, v)
			}
		}
		actual := config.createAuthHeader(req, timestamp, nonce)
		if assert.Equal(t, edge.ExpectedAuthorization, actual, fmt.Sprintf("Fail: %s", edge.Name)) {
			t.Logf("Pass: %s\n", edge.Name)
			t.Logf("Expected: %s - Actual %s", edge.ExpectedAuthorization, actual)
		}

	}
}

func TestAddRequestHeader(t *testing.T) {
	req, err := http.NewRequest("GET", config.Host, nil)
	if err != nil {
		t.Errorf("Fail: %s", err)
	}
	actual := AddRequestHeader(config, req)
	assert.NotEmpty(t, actual.Header.Get("Authorization"))
	assert.NotEmpty(t, actual.Header.Get("Content-Type"))
}

func TestInitConfigDefault(t *testing.T) {
	testSample := "sample_edgerc"
	testConfigDefault := InitConfig(testSample, "")
	assert.Equal(t, testConfigDefault.ClientToken, "xxxx-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx")
	assert.Equal(t, testConfigDefault.ClientSecret, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=")
	assert.Equal(t, testConfigDefault.AccessToken, "xxxx-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx")
	assert.Equal(t, testConfigDefault.MaxBody, 131072)
	assert.Equal(t, testConfigDefault.HeaderToSign, []string(nil))
}

func TestInitConfigBroken(t *testing.T) {
	testSample := "sample_edgerc"
	testConfigBroken := InitConfig(testSample, "broken")
	assert.Equal(t, testConfigBroken.ClientSecret, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=")
	assert.Equal(t, testConfigBroken.AccessToken, "xxxx-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx")
	assert.Equal(t, testConfigBroken.MaxBody, 128*1024)
	assert.Equal(t, testConfigBroken.HeaderToSign, []string(nil))
}

func TestInitConfigUnparsable(t *testing.T) {
	testSample := "edgerc_that_doesnt_parse"
	assert.Panics(t, func() { InitConfig(testSample, "") }, "Fail: Should raise a PANIC")
}

func TestInitConfigHeaders(t *testing.T) {
	testSample := "sample_edgerc"
	testConfigBroken := InitConfig(testSample, "headers")
	assert.Equal(t, testConfigBroken.HeaderToSign, []string{"X-MyThing1", "X-MyThing2"})
}

func TestInitConfigDashes(t *testing.T) {
	testSample := "sample_edgerc"
	assert.Panics(t, func() { InitConfig(testSample, "dashes") }, "Fail: Should raise a PANIC")
}
<commit_msg>testcase unknown file<commit_after>package edgegrid

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"testing"
)

var (
	testFile  = "testdata.json"
	timestamp = "20140321T19:34:21+0000"
	nonce     = "nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
	config    = Config{
		Host:         "https://akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net/",
		AccessToken:  "akab-access-token-xxx-xxxxxxxxxxxxxxxx",
		ClientToken:  "akab-client-token-xxx-xxxxxxxxxxxxxxxx",
		ClientSecret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=",
		MaxBody:      2048,
		Debug:        true,
		HeaderToSign: []string{
			"X-Test1",
			"X-Test2",
			"X-Test3",
		},
	}
)

type JsonTests struct {
	Tests []Test `json:"tests"`
}
type Test struct {
	Name    string `json:"testName"`
	Request struct {
		Method  string              `json:"method"`
		Path    string              `json:"path"`
		Headers []map[string]string `json:"headers"`
		Data    string              `json:"data"`
	} `json:"request"`
	ExpectedAuthorization string `json:"expectedAuthorization"`
}

func TestMakeEdgeTimeStamp(t *testing.T) {
	actual := makeEdgeTimeStamp()
	expected := regexp.MustCompile(`^\d{4}[0-1][0-9][0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\+0000$`)
	if assert.Regexp(t, expected, actual, "Fail: Regex do not match") {
		t.Log("Pass: Regex matches")
	}
}

func TestCreateNonce(t *testing.T) {
	actual := createNonce()
	for i := 0; i < 100; i++ {
		expected := createNonce()
		assert.NotEqual(t, actual, expected, "Fail: Nonce matches")
	}
}

func TestCreateAuthHeader(t *testing.T) {
	var edgegrid JsonTests
	byt, err := ioutil.ReadFile(testFile)
	if err != nil {
		t.Fatalf("Test file not found, err %s", err)
	}
	url, err := url.Parse(config.Host)
	if err != nil {
		t.Fatalf("URL is not parsable, err %s", err)
	}
	json.Unmarshal(byt, &edgegrid)
	for _, edge := range edgegrid.Tests {
		url.Path = edge.Request.Path
		req, _ := http.NewRequest(
			edge.Request.Method,
			url.String(),
			bytes.NewBuffer([]byte(edge.Request.Data)),
		)
		for _, header := range edge.Request.Headers {
			for k, v := range header {
				req.Header.Set(k, v)
			}
		}
		actual := config.createAuthHeader(req, timestamp, nonce)
		if assert.Equal(t, edge.ExpectedAuthorization, actual, fmt.Sprintf("Fail: %s", edge.Name)) {
			t.Logf("Pass: %s\n", edge.Name)
			t.Logf("Expected: %s - Actual %s", edge.ExpectedAuthorization, actual)
		}

	}
}

func TestAddRequestHeader(t *testing.T) {
	req, err := http.NewRequest("GET", config.Host, nil)
	if err != nil {
		t.Errorf("Fail: %s", err)
	}
	actual := AddRequestHeader(config, req)
	assert.NotEmpty(t, actual.Header.Get("Authorization"))
	assert.NotEmpty(t, actual.Header.Get("Content-Type"))
}

func TestInitConfigDefault(t *testing.T) {
	testSample := "sample_edgerc"
	testConfigDefault := InitConfig(testSample, "")
	assert.Equal(t, testConfigDefault.ClientToken, "xxxx-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx")
	assert.Equal(t, testConfigDefault.ClientSecret, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=")
	assert.Equal(t, testConfigDefault.AccessToken, "xxxx-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx")
	assert.Equal(t, testConfigDefault.MaxBody, 131072)
	assert.Equal(t, testConfigDefault.HeaderToSign, []string(nil))
}

func TestInitConfigBroken(t *testing.T) {
	testSample := "sample_edgerc"
	testConfigBroken := InitConfig(testSample, "broken")
	assert.Equal(t, testConfigBroken.ClientSecret, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=")
	assert.Equal(t, testConfigBroken.AccessToken, "xxxx-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx")
	assert.Equal(t, testConfigBroken.MaxBody, 128*1024)
	assert.Equal(t, testConfigBroken.HeaderToSign, []string(nil))
}

func TestInitConfigUnparsable(t *testing.T) {
	testSample := "edgerc_that_doesnt_parse"
	assert.Panics(t, func() { InitConfig(testSample, "") }, "Fail: Should raise a PANIC")
}

func TestInitConfigNotFound(t *testing.T) {
	testSample := "edgerc_not_found"
	assert.Panics(t, func() { InitConfig(testSample, "") }, "Fail: Should raise a PANIC")
}

func TestInitConfigHeaders(t *testing.T) {
	testSample := "sample_edgerc"
	testConfigBroken := InitConfig(testSample, "headers")
	assert.Equal(t, testConfigBroken.HeaderToSign, []string{"X-MyThing1", "X-MyThing2"})
}

func TestInitConfigDashes(t *testing.T) {
	testSample := "sample_edgerc"
	assert.Panics(t, func() { InitConfig(testSample, "dashes") }, "Fail: Should raise a PANIC")
}
<|endoftext|>