<commit_before>package main

// copyright(c) 2014, Jason E. Aten
//
// goq : a simple queueing system in go; qsub replacement.
//

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	//"syscall"
	"time"
)

func (w *Worker) Shepard(jobPtr *Job) {

	// to avoid data races, make a copy of the job value
	j := *jobPtr

	// reset our input channel
	w.DrainTellShepPidKilled()

	go func() {
		myPid := 0

		defer func() {
			// if myPid set then ShepSaysJobStarted already signalled.
			// otherwise indicate error:
			if myPid == 0 {
				w.ShepSaysJobStarted <- 0
			}
			WPrintf("end of SHEP: just before w.ShepSaysJobDone <- j\n")
			w.ShepSaysJobDone <- &j
			WPrintf("end of SHEP: just after w.ShepSaysJobDone <- j\n")
		}()

		jid := j.Id
		//dir := j.Dir
		subhome := j.HomeOnSubmitter
		dir := substituteHomeToWorkLocally(subhome, j.Dir)

		cmd := j.Cmd
		args := j.Args
		env := CreateShepardedEnv(j.Env)
		if j.Out == nil {
			j.Out = make([]string, 0)
		}
		var origdir string
		var err error
		origdir, err = os.Getwd()
		if err != nil {
			panic(err)
		}

		if dir != "" {
			err = os.Chdir(dir)
			if err != nil {
				errs := err.Error()
				if strings.Contains(errs, "system cannot find the path") &&
					strings.Contains(errs, "/cygdrive/") {
					// try to replace "/cygdrive/z" with "z:"
					dir = replaceCygdrive(dir)
					err = os.Chdir(dir)
				}
			}
			if err != nil {
				j.Out = append(j.Out, fmt.Sprintf("Shepard got error trying to move to submit directory with os.Chdir('%s'): %s", dir, err))
				return
			}

			// go back to our starting dir at the end of shepding this job.
			defer os.Chdir(origdir)
		}

		var path string
		var c *exec.Cmd
		path, err = MakeShellScript(cmd, args, dir)
		if err != nil {
			j.Out = append(j.Out, fmt.Sprintf("Shepard got error trying to create bash shell script in dir '%s': %s", dir, err))
			return
		}
		defer os.Remove(path)
		c = exec.Command(path)

		if runtime.GOOS == "windows" {
			// TODO: don't hard code bash location
			c = exec.Command("C:\\cygwin64\\bin\\bash.exe", path)
		}

		// old, not-run-in-shell style:
		//c = exec.Command(cmd, args...)

		c.Dir = dir
		c.Env = env

		var oe bytes.Buffer
		c.Stdout = &oe
		c.Stderr = &oe

		//	oe, err = c.CombinedOutput()

		// put in its own process group so all children cancel
		// if we cancel.
		systemCallSetGroup(c)

		err = c.Start()
		if err != nil {
			j.Out = append(j.Out, fmt.Sprintf("Shepard finds non-nil err on trying to Start() cmd '%s' in dir '%s': %s", cmd, dir, err))
			return
		}

		// no error/return should be possible between
		// setting myPid and w.ShepSaysJobStarted <- myPid
		myPid = c.Process.Pid
		j.Pid = int64(myPid)
		VPrintf("\n SHEP Shepard goroutine about to block on w.ShepSaysJobStarted <- j\n")
		w.ShepSaysJobStarted <- myPid
		VPrintf("\n SHEP Shepard goroutine about to block on c.Wait()\n")

		// this c.Wait() can be 15-20 seconds *slooooow*, so also wait on TellShepPidKilled
		//  to speed things up.
		err = nil
		// waitDone is buffered so this next short goro can exit immediately after c.Wait() finishes.
		// i.e. if TellShepPidKilled arrives first, then there will never be a receiver, so
		// without the buffering the channel and goroutine would be blocked, uncollectable, waiting-forever garbage/leak.
		waitDone := make(chan error, 1)
		go func() {
			err = c.Wait()
			waitDone <- err
		}()

		// back in shep goroutine:
		select {
		case err = <-waitDone:
		case killedPid := <-w.TellShepPidKilled:
			if killedPid != myPid {
				panic(fmt.Sprintf("SHEP error: mismatch in myPid(%d) vs killedPid(%d) received on w.TellShepPidKilled", myPid, killedPid))
			}
			j.Cancelled = true
			WPrintf("\n SHEP got notice from w.TellShepPidKilled, setting j.Cancelled = true\n")
		}

		// Now set j.Out based on which of the two cases we just saw:
		//  Either we saw w.TellShepPidKilled, in which case j.Cancelled == true and we want to exit quickly.
		//  Otherwise, we had a normal or fast c.Wait() exit, and we want to gather and send output on j.Out.
		//
		if j.Cancelled {
			j.Out = append(j.Out, fmt.Sprintf("cancelled/killed: job %d / pid %d ; cmd '%s' in dir '%s' on worker '%s' at '%s'", jid, myPid, cmd, dir, j.Workeraddr, time.Now()))

			// Don't wait around for output/etc.
			// Just skip down to ShepSaysJobDone and get out of here fast.

		} else {
			// Normal/Fast c.Wait() exit:
			WPrintf("\n SHEP DONE with WAIT, err: '%v'\n", err)
			if err != nil && err.Error() == "signal: killed" {
				WPrintf("\n SHEP found 'signal:killed', setting j.Cancelled = true\n")
				j.Cancelled = true
			}
			if err != nil {
				j.Err = err.Error()
				j.HadError = true
			}
			s := string(oe.Bytes())
			strings.Trim(s, "\n")
			slen := len(s)
			out := strings.Split(s, "\n")
			// if file ended in '\n' then we now have an extra empty line to eliminate.
			N := len(out)
			if slen > 0 && s[slen-1] == '\n' && N > 0 && out[N-1] == "" {
				out = out[:N-1]
			}
			j.Out = append(j.Out, out...)

			if err != nil {
				j.Out = append(j.Out, fmt.Sprintf("Shepard finds non-nil err on trying to Wait() on cmd '%s' in dir '%s': %s", cmd, dir, err))
			}
		}
	}()
}

func CreateShepardedEnv(jobenv []string) []string {
	// for now, ignore job sub-env and just use local worker env.
	return os.Environ()
}

func substituteHomeToWorkLocally(subHome, submitDir string) (localDir string) {
	if subHome == "" {
		return submitDir
	}
	localHome := os.Getenv("HOME")

	//defer func() {
	//vv("shep.go:182 substituteHomeToWorkLocally() localHome = '%v'; subHome='%v'; submitDir='%v' -> localDir='%v'", localHome, subHome, submitDir, localDir)
	//}()

	if !strings.HasPrefix(submitDir, subHome) {
		return submitDir
	}

	// detected the submitter's home directory in the path.
	// replace it with our local home directory.
	after := submitDir[len(subHome):]
	localDir = doubleSepToOne(sepOK(localHome + sep + after))
	return
}

func sepOK(s string) string {
	switch {
	case sep == `/`:
		return strings.Replace(s, `\`, `/`, -1)
	case sep == `\`:
		return strings.Replace(s, `/`, `\`, -1)
	}
	return s
}

func doubleSepToOne(s string) string {
	switch {
	case sep == `\`:
		return strings.Replace(s, `\\`, `\`, -1)
	case sep == `/`:
		return strings.Replace(s, `//`, `/`, -1)
	}
	return s
}

// "/cygdrive/z/hello" -> "z:\hello"
func replaceCygdrive(dir string) string {
	prefix := "/cygdrive/"
	if !strings.HasPrefix(dir, prefix) {
		return dir
	}
	red := dir[len(prefix):]
	if red[1] != '/' {
		return dir
	}
	driveLetter := red[:1]
	return sepOK(driveLetter + ":" + red[1:])
}

// "z:\hello" -> "/cygdrive/z/hello"
func replaceWindrive(dir string) string {
	if len(dir) < 3 || dir[1] != ':' {
		return dir
	}
	driveLetter := dir[:1] // "z"
	red := dir[2:]         // "\hello"
	return sepOK("/cygdrive/" + driveLetter + red)
}
<commit_msg>try  replaceWindrive in shep too<commit_after>package main

// copyright(c) 2014, Jason E. Aten
//
// goq : a simple queueing system in go; qsub replacement.
//

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	//"syscall"
	"time"
)

func (w *Worker) Shepard(jobPtr *Job) {

	// to avoid data races, make a copy of the job value
	j := *jobPtr

	// reset our input channel
	w.DrainTellShepPidKilled()

	go func() {
		myPid := 0

		defer func() {
			// if myPid set then ShepSaysJobStarted already signalled.
			// otherwise indicate error:
			if myPid == 0 {
				w.ShepSaysJobStarted <- 0
			}
			WPrintf("end of SHEP: just before w.ShepSaysJobDone <- j\n")
			w.ShepSaysJobDone <- &j
			WPrintf("end of SHEP: just after w.ShepSaysJobDone <- j\n")
		}()

		jid := j.Id
		//dir := j.Dir
		subhome := j.HomeOnSubmitter
		dir := substituteHomeToWorkLocally(subhome, j.Dir)

		cmd := j.Cmd
		args := j.Args
		env := CreateShepardedEnv(j.Env)
		if j.Out == nil {
			j.Out = make([]string, 0)
		}
		var origdir string
		var err error
		origdir, err = os.Getwd()
		if err != nil {
			panic(err)
		}

		if dir != "" {
			err = os.Chdir(dir)
			if err != nil {
				errs := err.Error()
				if strings.Contains(errs, "system cannot find the path") &&
					strings.Contains(errs, "/cygdrive/") {
					// try "/cygdrive/z" -> "z:"
					dir = replaceCygdrive(dir)
					err = os.Chdir(dir)
				}
				if strings.Contains(errs, "no such file") {
					// try "z:\hello" -> "/cygdrive/z/hello"
					dir = replaceWindrive(dir)
					err = os.Chdir(dir)
				}
			}
			if err != nil {
				j.Out = append(j.Out, fmt.Sprintf("Shepard got error trying to move to submit directory with os.Chdir('%s'): %s", dir, err))
				return
			}

			// go back to our starting dir at the end of shepding this job.
			defer os.Chdir(origdir)
		}

		var path string
		var c *exec.Cmd
		path, err = MakeShellScript(cmd, args, dir)
		if err != nil {
			j.Out = append(j.Out, fmt.Sprintf("Shepard got error trying to create bash shell script in dir '%s': %s", dir, err))
			return
		}
		defer os.Remove(path)
		c = exec.Command(path)

		if runtime.GOOS == "windows" {
			// TODO: don't hard code bash location
			c = exec.Command("C:\\cygwin64\\bin\\bash.exe", path)
		}

		// old, not-run-in-shell style:
		//c = exec.Command(cmd, args...)

		c.Dir = dir
		c.Env = env

		var oe bytes.Buffer
		c.Stdout = &oe
		c.Stderr = &oe

		//	oe, err = c.CombinedOutput()

		// put in its own process group so all children cancel
		// if we cancel.
		systemCallSetGroup(c)

		err = c.Start()
		if err != nil {
			j.Out = append(j.Out, fmt.Sprintf("Shepard finds non-nil err on trying to Start() cmd '%s' in dir '%s': %s", cmd, dir, err))
			return
		}

		// no error/return should be possible between
		// setting myPid and w.ShepSaysJobStarted <- myPid
		myPid = c.Process.Pid
		j.Pid = int64(myPid)
		VPrintf("\n SHEP Shepard goroutine about to block on w.ShepSaysJobStarted <- j\n")
		w.ShepSaysJobStarted <- myPid
		VPrintf("\n SHEP Shepard goroutine about to block on c.Wait()\n")

		// this c.Wait() can be 15-20 seconds *slooooow*, so also wait on TellShepPidKilled
		//  to speed things up.
		err = nil
		// waitDone is buffered so this next short goro can exit immediately after c.Wait() finishes.
		// i.e. if TellShepPidKilled arrives first, then there will never be a receiver, so
		// without the buffering the channel and goroutine would be blocked, uncollectable, waiting-forever garbage/leak.
		waitDone := make(chan error, 1)
		go func() {
			err = c.Wait()
			waitDone <- err
		}()

		// back in shep goroutine:
		select {
		case err = <-waitDone:
		case killedPid := <-w.TellShepPidKilled:
			if killedPid != myPid {
				panic(fmt.Sprintf("SHEP error: mismatch in myPid(%d) vs killedPid(%d) received on w.TellShepPidKilled", myPid, killedPid))
			}
			j.Cancelled = true
			WPrintf("\n SHEP got notice from w.TellShepPidKilled, setting j.Cancelled = true\n")
		}

		// Now set j.Out based on which of the two cases we just saw:
		//  Either we saw w.TellShepPidKilled, in which case j.Cancelled == true and we want to exit quickly.
		//  Otherwise, we had a normal or fast c.Wait() exit, and we want to gather and send output on j.Out.
		//
		if j.Cancelled {
			j.Out = append(j.Out, fmt.Sprintf("cancelled/killed: job %d / pid %d ; cmd '%s' in dir '%s' on worker '%s' at '%s'", jid, myPid, cmd, dir, j.Workeraddr, time.Now()))

			// Don't wait around for output/etc.
			// Just skip down to ShepSaysJobDone and get out of here fast.

		} else {
			// Normal/Fast c.Wait() exit:
			WPrintf("\n SHEP DONE with WAIT, err: '%v'\n", err)
			if err != nil && err.Error() == "signal: killed" {
				WPrintf("\n SHEP found 'signal:killed', setting j.Cancelled = true\n")
				j.Cancelled = true
			}
			if err != nil {
				j.Err = err.Error()
				j.HadError = true
			}
			s := string(oe.Bytes())
			strings.Trim(s, "\n")
			slen := len(s)
			out := strings.Split(s, "\n")
			// if file ended in '\n' then we now have an extra empty line to eliminate.
			N := len(out)
			if slen > 0 && s[slen-1] == '\n' && N > 0 && out[N-1] == "" {
				out = out[:N-1]
			}
			j.Out = append(j.Out, out...)

			if err != nil {
				j.Out = append(j.Out, fmt.Sprintf("Shepard finds non-nil err on trying to Wait() on cmd '%s' in dir '%s': %s", cmd, dir, err))
			}
		}
	}()
}

func CreateShepardedEnv(jobenv []string) []string {
	// for now, ignore job sub-env and just use local worker env.
	return os.Environ()
}

func substituteHomeToWorkLocally(subHome, submitDir string) (localDir string) {
	if subHome == "" {
		return submitDir
	}
	localHome := os.Getenv("HOME")

	//defer func() {
	//vv("shep.go:182 substituteHomeToWorkLocally() localHome = '%v'; subHome='%v'; submitDir='%v' -> localDir='%v'", localHome, subHome, submitDir, localDir)
	//}()

	if !strings.HasPrefix(submitDir, subHome) {
		return submitDir
	}

	// detected the submitter's home directory in the path.
	// replace it with our local home directory.
	after := submitDir[len(subHome):]
	localDir = doubleSepToOne(sepOK(localHome + sep + after))
	return
}

func sepOK(s string) string {
	switch {
	case sep == `/`:
		return strings.Replace(s, `\`, `/`, -1)
	case sep == `\`:
		return strings.Replace(s, `/`, `\`, -1)
	}
	return s
}

func doubleSepToOne(s string) string {
	switch {
	case sep == `\`:
		return strings.Replace(s, `\\`, `\`, -1)
	case sep == `/`:
		return strings.Replace(s, `//`, `/`, -1)
	}
	return s
}

// "/cygdrive/z/hello" -> "z:\hello"
func replaceCygdrive(dir string) string {
	prefix := "/cygdrive/"
	if !strings.HasPrefix(dir, prefix) {
		return dir
	}
	red := dir[len(prefix):]
	if red[1] != '/' {
		return dir
	}
	driveLetter := red[:1]
	return sepOK(driveLetter + ":" + red[1:])
}

// "z:\hello" -> "/cygdrive/z/hello"
func replaceWindrive(dir string) string {
	if len(dir) < 3 || dir[1] != ':' {
		return dir
	}
	driveLetter := dir[:1] // "z"
	red := dir[2:]         // "\hello"
	return sepOK("/cygdrive/" + driveLetter + red)
}
<|endoftext|>


<commit_before>package libkb

import (
	"code.google.com/p/go.crypto/openpgp"
	"code.google.com/p/go.crypto/openpgp/armor"
	"code.google.com/p/go.crypto/openpgp/errors"
	"code.google.com/p/go.crypto/openpgp/packet"
	"crypto"
	"hash"
	"io"
	"time"
)

//
// primaryIdentity returns the Identity marked as primary or the first identity
// if none are so marked.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#75
//   func (e *Entity) primaryIdentity() *Identity
//
func getPrimaryIdentity(e *openpgp.Entity) *openpgp.Identity {
	var firstIdentity *openpgp.Identity
	for _, ident := range e.Identities {
		if firstIdentity == nil {
			firstIdentity = ident
		}
		if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
			return ident
		}
	}
	return firstIdentity
}

//
// signingKey return the best candidate Key for signing a message with this Entity.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#125
//   func (e *Entity) signingKey(now time.Time) (Key, bool)
//
func getSigningKey(e *openpgp.Entity, now time.Time) (openpgp.Key, bool) {
	candidateSubkey := -1

	for i, subkey := range e.Subkeys {
		if subkey.Sig.FlagsValid &&
			subkey.Sig.FlagSign &&
			subkey.PublicKey.PubKeyAlgo.CanSign() &&
			!subkey.Sig.KeyExpired(now) {
			candidateSubkey = i
			break
		}
	}

	if candidateSubkey != -1 {
		subkey := e.Subkeys[candidateSubkey]
		return openpgp.Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
	}

	// If we have no candidate subkey then we assume that it's ok to sign
	// with the primary key.
	i := getPrimaryIdentity(e)
	if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
		!i.SelfSignature.KeyExpired(now) {
		return openpgp.Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
	}

	return openpgp.Key{}, false
}

// Like openpgp.Encrypt (as in p.crypto/openpgp/write.go), but
// don't encrypt at all, just sign the literal unencrypted data.
// Unfortunately we need to duplicate some code here that's already
// in write.go
func AttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	if hints == nil {
		hints = &openpgp.FileHints{}
	}

	if config == nil {
		config = &packet.Config{}
	}

	var signer *packet.PrivateKey

	if signKey, ok := getSigningKey(&signed, config.Now()); !ok {
		err = errors.InvalidArgumentError("no valid signing keys")
		return
	} else if signer = signKey.PrivateKey; signer.Encrypted {
		err = errors.InvalidArgumentError("signing key must be decrypted")
		return
	}

	hasher := crypto.SHA512

	ops := &packet.OnePassSignature{
		SigType:    packet.SigTypeBinary,
		Hash:       hasher,
		PubKeyAlgo: signer.PubKeyAlgo,
		KeyId:      signer.KeyId,
		IsLast:     true,
	}

	if err = ops.Serialize(out); err != nil {
		return
	}

	var epochSeconds uint32
	if !hints.ModTime.IsZero() {
		epochSeconds = uint32(hints.ModTime.Unix())
	}

	in, err = packet.SerializeLiteral(out, hints.IsBinary, hints.FileName, epochSeconds)

	if err != nil {
		return
	}

	in = signatureWriter{out, in, hasher, hasher.New(), signer, config}

	return
}

func ArmoredAttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	var aout io.WriteCloser
	aout, err = armor.Encode(out, "PGP MESSAGE", PgpArmorHeaders())
	if err != nil {
		return
	}
	return AttachedSign(aout, signed, hints, config)
}

func AttachedSignWrapper(out io.WriteCloser, key PgpKeyBundle, armored bool) (
	in io.WriteCloser, err error) {

	if armored {
		return ArmoredAttachedSign(out, openpgp.Entity(key), nil, nil)
	} else {
		return AttachedSign(out, openpgp.Entity(key), nil, nil)
	}
}

// From here:
//   https://code.google.com/p/go/source/browse/openpgp/write.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#326
//
// signatureWriter hashes the contents of a message while passing it along to
// literalData. When closed, it closes literalData, writes a signature packet
// to encryptedData and then also closes encryptedData.
type signatureWriter struct {
	signedData  io.WriteCloser
	literalData io.WriteCloser
	hashType    crypto.Hash
	h           hash.Hash
	signer      *packet.PrivateKey
	config      *packet.Config
}

func (s signatureWriter) Write(data []byte) (int, error) {
	s.h.Write(data)
	return s.literalData.Write(data)
}

func (s signatureWriter) Close() error {
	sig := &packet.Signature{
		SigType:      packet.SigTypeBinary,
		PubKeyAlgo:   s.signer.PubKeyAlgo,
		Hash:         s.hashType,
		CreationTime: s.config.Now(),
		IssuerKeyId:  &s.signer.KeyId,
	}

	if err := sig.Sign(s.h, s.signer, s.config); err != nil {
		return err
	}
	if err := s.literalData.Close(); err != nil {
		return err
	}
	if err := sig.Serialize(s.signedData); err != nil {
		return err
	}
	return s.signedData.Close()
}

// From here:
type noOpCloser struct {
	w io.Writer
}

func (c noOpCloser) Write(data []byte) (n int, err error) {
	return c.w.Write(data)
}

func (c noOpCloser) Close() error {
	return nil
}
<commit_msg>closer<commit_after>package libkb

import (
	"code.google.com/p/go.crypto/openpgp"
	"code.google.com/p/go.crypto/openpgp/armor"
	"code.google.com/p/go.crypto/openpgp/errors"
	"code.google.com/p/go.crypto/openpgp/packet"
	"crypto"
	"hash"
	"io"
	"time"
)

//
// primaryIdentity returns the Identity marked as primary or the first identity
// if none are so marked.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#75
//   func (e *Entity) primaryIdentity() *Identity
//
func getPrimaryIdentity(e *openpgp.Entity) *openpgp.Identity {
	var firstIdentity *openpgp.Identity
	for _, ident := range e.Identities {
		if firstIdentity == nil {
			firstIdentity = ident
		}
		if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
			return ident
		}
	}
	return firstIdentity
}

//
// signingKey return the best candidate Key for signing a message with this Entity.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#125
//   func (e *Entity) signingKey(now time.Time) (Key, bool)
//
func getSigningKey(e *openpgp.Entity, now time.Time) (openpgp.Key, bool) {
	candidateSubkey := -1

	for i, subkey := range e.Subkeys {
		if subkey.Sig.FlagsValid &&
			subkey.Sig.FlagSign &&
			subkey.PublicKey.PubKeyAlgo.CanSign() &&
			!subkey.Sig.KeyExpired(now) {
			candidateSubkey = i
			break
		}
	}

	if candidateSubkey != -1 {
		subkey := e.Subkeys[candidateSubkey]
		return openpgp.Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
	}

	// If we have no candidate subkey then we assume that it's ok to sign
	// with the primary key.
	i := getPrimaryIdentity(e)
	if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
		!i.SelfSignature.KeyExpired(now) {
		return openpgp.Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
	}

	return openpgp.Key{}, false
}

// Like openpgp.Encrypt (as in p.crypto/openpgp/write.go), but
// don't encrypt at all, just sign the literal unencrypted data.
// Unfortunately we need to duplicate some code here that's already
// in write.go
func AttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	if hints == nil {
		hints = &openpgp.FileHints{}
	}

	if config == nil {
		config = &packet.Config{}
	}

	var signer *packet.PrivateKey

	if signKey, ok := getSigningKey(&signed, config.Now()); !ok {
		err = errors.InvalidArgumentError("no valid signing keys")
		return
	} else if signer = signKey.PrivateKey; signer.Encrypted {
		err = errors.InvalidArgumentError("signing key must be decrypted")
		return
	}

	hasher := crypto.SHA512

	ops := &packet.OnePassSignature{
		SigType:    packet.SigTypeBinary,
		Hash:       hasher,
		PubKeyAlgo: signer.PubKeyAlgo,
		KeyId:      signer.KeyId,
		IsLast:     true,
	}

	if err = ops.Serialize(out); err != nil {
		return
	}

	var epochSeconds uint32
	if !hints.ModTime.IsZero() {
		epochSeconds = uint32(hints.ModTime.Unix())
	}

	in, err = packet.SerializeLiteral(out, hints.IsBinary, hints.FileName, epochSeconds)

	if err != nil {
		return
	}

	in = signatureWriter{out, noOpCloser{in}, hasher, hasher.New(), signer, config}

	return
}

type DebugWriteCloser struct {
	targ io.WriteCloser
}

func (dbc DebugWriteCloser) Write(buf []byte) (int, error) {
	G.Log.Debug("dbc write: %v", buf)
	return dbc.targ.Write(buf)
}

func (dbc DebugWriteCloser) Close() error {
	G.Log.Debug("dbc Close!")
	return dbc.targ.Close()
}

func ArmoredAttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	var aout io.WriteCloser
	aout, err = armor.Encode(out, "PGP MESSAGE", PgpArmorHeaders())
	if err != nil {
		return
	}
	return AttachedSign(DebugWriteCloser{aout}, signed, hints, config)
}

func AttachedSignWrapper(out io.WriteCloser, key PgpKeyBundle, armored bool) (
	in io.WriteCloser, err error) {

	if armored {
		return ArmoredAttachedSign(out, openpgp.Entity(key), nil, nil)
	} else {
		return AttachedSign(out, openpgp.Entity(key), nil, nil)
	}
}

// From here:
//   https://code.google.com/p/go/source/browse/openpgp/write.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#326
//
// signatureWriter hashes the contents of a message while passing it along to
// literalData. When closed, it closes literalData, writes a signature packet
// to encryptedData and then also closes encryptedData.
type signatureWriter struct {
	signedData  io.WriteCloser
	literalData io.WriteCloser
	hashType    crypto.Hash
	h           hash.Hash
	signer      *packet.PrivateKey
	config      *packet.Config
}

func (s signatureWriter) Write(data []byte) (int, error) {
	s.h.Write(data)
	return s.literalData.Write(data)
}

func (s signatureWriter) Close() error {
	sig := &packet.Signature{
		SigType:      packet.SigTypeBinary,
		PubKeyAlgo:   s.signer.PubKeyAlgo,
		Hash:         s.hashType,
		CreationTime: s.config.Now(),
		IssuerKeyId:  &s.signer.KeyId,
	}

	if err := sig.Sign(s.h, s.signer, s.config); err != nil {
		return err
	}
	if err := s.literalData.Close(); err != nil {
		return err
	}
	if err := sig.Serialize(s.signedData); err != nil {
		return err
	}
	return s.signedData.Close()
}

// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
// TODO: we have two of these in OpenPGP packages alone. This probably needs
// to be promoted somewhere more common.
type noOpCloser struct {
	w io.Writer
}

func (c noOpCloser) Write(data []byte) (n int, err error) {
	return c.w.Write(data)
}

func (c noOpCloser) Close() error {
	return nil
}
<|endoftext|>


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

package github

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"testing"
)

func TestValidatePayload(t *testing.T) {
	const defaultBody = `{"yo":true}` // All tests below use the default request body and signature.
	const defaultSignature = "sha1=126f2c800419c60137ce748d7672e77b65cf16d6"
	secretKey := []byte("0123456789abcdef")
	tests := []struct {
		signature   string
		eventID     string
		event       string
		wantEventID string
		wantEvent   string
		wantPayload string
	}{
		// The following tests generate expected errors:
		{},                         // Missing signature
		{signature: "yo"},          // Missing signature prefix
		{signature: "sha1=yo"},     // Signature not hex string
		{signature: "sha1=012345"}, // Invalid signature
		// The following tests expect err=nil:
		{
			signature:   defaultSignature,
			eventID:     "dead-beef",
			event:       "ping",
			wantEventID: "dead-beef",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
		{
			signature:   defaultSignature,
			event:       "ping",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
		{
			signature:   "sha256=b1f8020f5b4cd42042f807dd939015c4a418bc1ff7f604dd55b0a19b5d953d9b",
			event:       "ping",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
		{
			signature:   "sha512=8456767023c1195682e182a23b3f5d19150ecea598fde8cb85918f7281b16079471b1329f92b912c4d8bd7455cb159777db8f29608b20c7c87323ba65ae62e1f",
			event:       "ping",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
	}

	for _, test := range tests {
		buf := bytes.NewBufferString(defaultBody)
		req, err := http.NewRequest("GET", "http://localhost/event", buf)
		if err != nil {
			t.Fatalf("NewRequest: %v", err)
		}
		if test.signature != "" {
			req.Header.Set(signatureHeader, test.signature)
		}
		req.Header.Set("Content-Type", "application/json")

		got, err := ValidatePayload(req, secretKey)
		if err != nil {
			if test.wantPayload != "" {
				t.Errorf("ValidatePayload(%#v): err = %v, want nil", test, err)
			}
			continue
		}
		if string(got) != test.wantPayload {
			t.Errorf("ValidatePayload = %q, want %q", got, test.wantPayload)
		}
	}
}

func TestValidatePayload_FormGet(t *testing.T) {
	payload := `{"yo":true}`
	signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368"
	secretKey := []byte("0123456789abcdef")

	form := url.Values{}
	form.Add("payload", payload)
	req, err := http.NewRequest("POST", "http://localhost/event", strings.NewReader(form.Encode()))
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.PostForm = form
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set(signatureHeader, signature)

	got, err := ValidatePayload(req, secretKey)
	if err != nil {
		t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err)
	}
	if string(got) != payload {
		t.Errorf("ValidatePayload = %q, want %q", got, payload)
	}

	// check that if payload is invalid we get error
	req.Header.Set(signatureHeader, "invalid signature")
	if _, err = ValidatePayload(req, []byte{0}); err == nil {
		t.Error("ValidatePayload = nil, want err")
	}
}

func TestValidatePayload_FormPost(t *testing.T) {
	payload := `{"yo":true}`
	signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368"
	secretKey := []byte("0123456789abcdef")

	form := url.Values{}
	form.Set("payload", payload)
	buf := bytes.NewBufferString(form.Encode())
	req, err := http.NewRequest("POST", "http://localhost/event", buf)
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set(signatureHeader, signature)

	got, err := ValidatePayload(req, secretKey)
	if err != nil {
		t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err)
	}
	if string(got) != payload {
		t.Errorf("ValidatePayload = %q, want %q", got, payload)
	}

	// check that if payload is invalid we get error
	req.Header.Set(signatureHeader, "invalid signature")
	if _, err = ValidatePayload(req, []byte{0}); err == nil {
		t.Error("ValidatePayload = nil, want err")
	}
}

func TestValidatePayload_InvalidContentType(t *testing.T) {
	req, err := http.NewRequest("POST", "http://localhost/event", nil)
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.Header.Set("Content-Type", "invalid content type")
	if _, err = ValidatePayload(req, nil); err == nil {
		t.Error("ValidatePayload = nil, want err")
	}
}

func TestValidatePayload_NoSecretKey(t *testing.T) {
	payload := `{"yo":true}`

	form := url.Values{}
	form.Set("payload", payload)
	buf := bytes.NewBufferString(form.Encode())
	req, err := http.NewRequest("POST", "http://localhost/event", buf)
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	got, err := ValidatePayload(req, nil)
	if err != nil {
		t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err)
	}
	if string(got) != payload {
		t.Errorf("ValidatePayload = %q, want %q", got, payload)
	}
}

func TestParseWebHook(t *testing.T) {
	tests := []struct {
		payload     interface{}
		messageType string
	}{
		{
			payload:     &CheckRunEvent{},
			messageType: "check_run",
		},
		{
			payload:     &CheckSuiteEvent{},
			messageType: "check_suite",
		},
		{
			payload:     &CommitCommentEvent{},
			messageType: "commit_comment",
		},
		{
			payload:     &ContentReferenceEvent{},
			messageType: "content_reference",
		},
		{
			payload:     &CreateEvent{},
			messageType: "create",
		},
		{
			payload:     &DeleteEvent{},
			messageType: "delete",
		},
		{
			payload:     &DeployKeyEvent{},
			messageType: "deploy_key",
		},
		{
			payload:     &DeploymentEvent{},
			messageType: "deployment",
		},

		{
			payload:     &DeploymentStatusEvent{},
			messageType: "deployment_status",
		},
		{
			payload:     &ForkEvent{},
			messageType: "fork",
		},
		{
			payload:     &GitHubAppAuthorizationEvent{},
			messageType: "github_app_authorization",
		},
		{
			payload:     &GollumEvent{},
			messageType: "gollum",
		},
		{
			payload:     &InstallationEvent{},
			messageType: "installation",
		},
		{
			payload:     &InstallationRepositoriesEvent{},
			messageType: "installation_repositories",
		},
		{
			payload:     &IssueCommentEvent{},
			messageType: "issue_comment",
		},
		{
			payload:     &IssuesEvent{},
			messageType: "issues",
		},
		{
			payload:     &LabelEvent{},
			messageType: "label",
		},
		{
			payload:     &MarketplacePurchaseEvent{},
			messageType: "marketplace_purchase",
		},
		{
			payload:     &MemberEvent{},
			messageType: "member",
		},
		{
			payload:     &MembershipEvent{},
			messageType: "membership",
		},
		{
			payload:     &MetaEvent{},
			messageType: "meta",
		},
		{
			payload:     &MilestoneEvent{},
			messageType: "milestone",
		},
		{
			payload:     &OrganizationEvent{},
			messageType: "organization",
		},
		{
			payload:     &OrgBlockEvent{},
			messageType: "org_block",
		},
		{
			payload:     &PackageEvent{},
			messageType: "package",
		},
		{
			payload:     &PageBuildEvent{},
			messageType: "page_build",
		},
		{
			payload:     &PingEvent{},
			messageType: "ping",
		},
		{
			payload:     &ProjectEvent{},
			messageType: "project",
		},
		{
			payload:     &ProjectCardEvent{},
			messageType: "project_card",
		},
		{
			payload:     &ProjectColumnEvent{},
			messageType: "project_column",
		},
		{
			payload:     &PublicEvent{},
			messageType: "public",
		},
		{
			payload:     &PullRequestEvent{},
			messageType: "pull_request",
		},
		{
			payload:     &PullRequestReviewEvent{},
			messageType: "pull_request_review",
		},
		{
			payload:     &PullRequestReviewCommentEvent{},
			messageType: "pull_request_review_comment",
		},
		{
			payload:     &PushEvent{},
			messageType: "push",
		},
		{
			payload:     &ReleaseEvent{},
			messageType: "release",
		},
		{
			payload:     &RepositoryEvent{},
			messageType: "repository",
		},
		{
			payload:     &RepositoryVulnerabilityAlertEvent{},
			messageType: "repository_vulnerability_alert",
		},
		{
			payload:     &StarEvent{},
			messageType: "star",
		},
		{
			payload:     &StatusEvent{},
			messageType: "status",
		},
		{
			payload:     &TeamEvent{},
			messageType: "team",
		},
		{
			payload:     &TeamAddEvent{},
			messageType: "team_add",
		},
		{
			payload:     &UserEvent{},
			messageType: "user",
		},
		{
			payload:     &WatchEvent{},
			messageType: "watch",
		},
		{
			payload:     &RepositoryDispatchEvent{},
			messageType: "repository_dispatch",
		},
		{
			payload:     &WorkflowDispatchEvent{},
			messageType: "workflow_dispatch",
		},
		{
			payload:     &WorkflowRunEvent{},
			messageType: "workflow_run",
		},
	}

	for _, test := range tests {
		p, err := json.Marshal(test.payload)
		if err != nil {
			t.Fatalf("Marshal(%#v): %v", test.payload, err)
		}
		got, err := ParseWebHook(test.messageType, p)
		if err != nil {
			t.Fatalf("ParseWebHook: %v", err)
		}
		if want := test.payload; !reflect.DeepEqual(got, want) {
			t.Errorf("ParseWebHook(%#v, %#v) = %#v, want %#v", test.messageType, p, got, want)
		}
	}
}

func TestDeliveryID(t *testing.T) {
	id := "8970a780-244e-11e7-91ca-da3aabcb9793"
	req, err := http.NewRequest("POST", "http://localhost", nil)
	if err != nil {
		t.Fatalf("DeliveryID: %v", err)
	}
	req.Header.Set("X-Github-Delivery", id)

	got := DeliveryID(req)
	if got != id {
		t.Errorf("DeliveryID(%#v) = %q, want %q", req, got, id)
	}
}
<commit_msg>Improve messages.go code coverage (#1671)<commit_after>// Copyright 2016 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"testing"
)

func TestMessageMAC_BadHashTypePrefix(t *testing.T) {
	const signature = "bogus1=1234567"
	if _, _, err := messageMAC(signature); err == nil {
		t.Fatal("messageMAC returned nil; wanted error")
	}
}

func TestValidatePayload(t *testing.T) {
	const defaultBody = `{"yo":true}` // All tests below use the default request body and signature.
	const defaultSignature = "sha1=126f2c800419c60137ce748d7672e77b65cf16d6"
	secretKey := []byte("0123456789abcdef")
	tests := []struct {
		signature   string
		eventID     string
		event       string
		wantEventID string
		wantEvent   string
		wantPayload string
	}{
		// The following tests generate expected errors:
		{},                         // Missing signature
		{signature: "yo"},          // Missing signature prefix
		{signature: "sha1=yo"},     // Signature not hex string
		{signature: "sha1=012345"}, // Invalid signature
		// The following tests expect err=nil:
		{
			signature:   defaultSignature,
			eventID:     "dead-beef",
			event:       "ping",
			wantEventID: "dead-beef",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
		{
			signature:   defaultSignature,
			event:       "ping",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
		{
			signature:   "sha256=b1f8020f5b4cd42042f807dd939015c4a418bc1ff7f604dd55b0a19b5d953d9b",
			event:       "ping",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
		{
			signature:   "sha512=8456767023c1195682e182a23b3f5d19150ecea598fde8cb85918f7281b16079471b1329f92b912c4d8bd7455cb159777db8f29608b20c7c87323ba65ae62e1f",
			event:       "ping",
			wantEvent:   "ping",
			wantPayload: defaultBody,
		},
	}

	for _, test := range tests {
		buf := bytes.NewBufferString(defaultBody)
		req, err := http.NewRequest("GET", "http://localhost/event", buf)
		if err != nil {
			t.Fatalf("NewRequest: %v", err)
		}
		if test.signature != "" {
			req.Header.Set(signatureHeader, test.signature)
		}
		req.Header.Set("Content-Type", "application/json")

		got, err := ValidatePayload(req, secretKey)
		if err != nil {
			if test.wantPayload != "" {
				t.Errorf("ValidatePayload(%#v): err = %v, want nil", test, err)
			}
			continue
		}
		if string(got) != test.wantPayload {
			t.Errorf("ValidatePayload = %q, want %q", got, test.wantPayload)
		}
	}
}

func TestValidatePayload_FormGet(t *testing.T) {
	payload := `{"yo":true}`
	signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368"
	secretKey := []byte("0123456789abcdef")

	form := url.Values{}
	form.Add("payload", payload)
	req, err := http.NewRequest("POST", "http://localhost/event", strings.NewReader(form.Encode()))
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.PostForm = form
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set(signatureHeader, signature)

	got, err := ValidatePayload(req, secretKey)
	if err != nil {
		t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err)
	}
	if string(got) != payload {
		t.Errorf("ValidatePayload = %q, want %q", got, payload)
	}

	// check that if payload is invalid we get error
	req.Header.Set(signatureHeader, "invalid signature")
	if _, err = ValidatePayload(req, []byte{0}); err == nil {
		t.Error("ValidatePayload = nil, want err")
	}
}

func TestValidatePayload_FormPost(t *testing.T) {
	payload := `{"yo":true}`
	signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368"
	secretKey := []byte("0123456789abcdef")

	form := url.Values{}
	form.Set("payload", payload)
	buf := bytes.NewBufferString(form.Encode())
	req, err := http.NewRequest("POST", "http://localhost/event", buf)
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set(signatureHeader, signature)

	got, err := ValidatePayload(req, secretKey)
	if err != nil {
		t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err)
	}
	if string(got) != payload {
		t.Errorf("ValidatePayload = %q, want %q", got, payload)
	}

	// check that if payload is invalid we get error
	req.Header.Set(signatureHeader, "invalid signature")
	if _, err = ValidatePayload(req, []byte{0}); err == nil {
		t.Error("ValidatePayload = nil, want err")
	}
}

func TestValidatePayload_InvalidContentType(t *testing.T) {
	req, err := http.NewRequest("POST", "http://localhost/event", nil)
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.Header.Set("Content-Type", "invalid content type")
	if _, err = ValidatePayload(req, nil); err == nil {
		t.Error("ValidatePayload = nil, want err")
	}
}

func TestValidatePayload_NoSecretKey(t *testing.T) {
	payload := `{"yo":true}`

	form := url.Values{}
	form.Set("payload", payload)
	buf := bytes.NewBufferString(form.Encode())
	req, err := http.NewRequest("POST", "http://localhost/event", buf)
	if err != nil {
		t.Fatalf("NewRequest: %v", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	got, err := ValidatePayload(req, nil)
	if err != nil {
		t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err)
	}
	if string(got) != payload {
		t.Errorf("ValidatePayload = %q, want %q", got, payload)
	}
}

// badReader satisfies io.Reader but always returns an error.
type badReader struct{}

func (b *badReader) Read(p []byte) (int, error) {
	return 0, errors.New("bad reader")
}

func (b *badReader) Close() error { return errors.New("bad reader") }

func TestValidatePayload_BadRequestBody(t *testing.T) {
	tests := []struct {
		contentType string
	}{
		{contentType: "application/json"},
		{contentType: "application/x-www-form-urlencoded"},
	}

	for i, tt := range tests {
		t.Run(fmt.Sprintf("test #%v", i), func(t *testing.T) {
			req := &http.Request{
				Header: http.Header{"Content-Type": []string{tt.contentType}},
				Body:   &badReader{},
			}
			if _, err := ValidatePayload(req, nil); err == nil {
				t.Fatal("ValidatePayload returned nil; want error")
			}
		})
	}
}

func TestParseWebHook(t *testing.T) {
	tests := []struct {
		payload     interface{}
		messageType string
	}{
		{
			payload:     &CheckRunEvent{},
			messageType: "check_run",
		},
		{
			payload:     &CheckSuiteEvent{},
			messageType: "check_suite",
		},
		{
			payload:     &CommitCommentEvent{},
			messageType: "commit_comment",
		},
		{
			payload:     &ContentReferenceEvent{},
			messageType: "content_reference",
		},
		{
			payload:     &CreateEvent{},
			messageType: "create",
		},
		{
			payload:     &DeleteEvent{},
			messageType: "delete",
		},
		{
			payload:     &DeployKeyEvent{},
			messageType: "deploy_key",
		},
		{
			payload:     &DeploymentEvent{},
			messageType: "deployment",
		},

		{
			payload:     &DeploymentStatusEvent{},
			messageType: "deployment_status",
		},
		{
			payload:     &ForkEvent{},
			messageType: "fork",
		},
		{
			payload:     &GitHubAppAuthorizationEvent{},
			messageType: "github_app_authorization",
		},
		{
			payload:     &GollumEvent{},
			messageType: "gollum",
		},
		{
			payload:     &InstallationEvent{},
			messageType: "installation",
		},
		{
			payload:     &InstallationRepositoriesEvent{},
			messageType: "installation_repositories",
		},
		{
			payload:     &IssueCommentEvent{},
			messageType: "issue_comment",
		},
		{
			payload:     &IssuesEvent{},
			messageType: "issues",
		},
		{
			payload:     &LabelEvent{},
			messageType: "label",
		},
		{
			payload:     &MarketplacePurchaseEvent{},
			messageType: "marketplace_purchase",
		},
		{
			payload:     &MemberEvent{},
			messageType: "member",
		},
		{
			payload:     &MembershipEvent{},
			messageType: "membership",
		},
		{
			payload:     &MetaEvent{},
			messageType: "meta",
		},
		{
			payload:     &MilestoneEvent{},
			messageType: "milestone",
		},
		{
			payload:     &OrganizationEvent{},
			messageType: "organization",
		},
		{
			payload:     &OrgBlockEvent{},
			messageType: "org_block",
		},
		{
			payload:     &PackageEvent{},
			messageType: "package",
		},
		{
			payload:     &PageBuildEvent{},
			messageType: "page_build",
		},
		{
			payload:     &PingEvent{},
			messageType: "ping",
		},
		{
			payload:     &ProjectEvent{},
			messageType: "project",
		},
		{
			payload:     &ProjectCardEvent{},
			messageType: "project_card",
		},
		{
			payload:     &ProjectColumnEvent{},
			messageType: "project_column",
		},
		{
			payload:     &PublicEvent{},
			messageType: "public",
		},
		{
			payload:     &PullRequestEvent{},
			messageType: "pull_request",
		},
		{
			payload:     &PullRequestReviewEvent{},
			messageType: "pull_request_review",
		},
		{
			payload:     &PullRequestReviewCommentEvent{},
			messageType: "pull_request_review_comment",
		},
		{
			payload:     &PushEvent{},
			messageType: "push",
		},
		{
			payload:     &ReleaseEvent{},
			messageType: "release",
		},
		{
			payload:     &RepositoryEvent{},
			messageType: "repository",
		},
		{
			payload:     &RepositoryVulnerabilityAlertEvent{},
			messageType: "repository_vulnerability_alert",
		},
		{
			payload:     &StarEvent{},
			messageType: "star",
		},
		{
			payload:     &StatusEvent{},
			messageType: "status",
		},
		{
			payload:     &TeamEvent{},
			messageType: "team",
		},
		{
			payload:     &TeamAddEvent{},
			messageType: "team_add",
		},
		{
			payload:     &UserEvent{},
			messageType: "user",
		},
		{
			payload:     &WatchEvent{},
			messageType: "watch",
		},
		{
			payload:     &RepositoryDispatchEvent{},
			messageType: "repository_dispatch",
		},
		{
			payload:     &WorkflowDispatchEvent{},
			messageType: "workflow_dispatch",
		},
		{
			payload:     &WorkflowRunEvent{},
			messageType: "workflow_run",
		},
	}

	for _, test := range tests {
		p, err := json.Marshal(test.payload)
		if err != nil {
			t.Fatalf("Marshal(%#v): %v", test.payload, err)
		}
		got, err := ParseWebHook(test.messageType, p)
		if err != nil {
			t.Fatalf("ParseWebHook: %v", err)
		}
		if want := test.payload; !reflect.DeepEqual(got, want) {
			t.Errorf("ParseWebHook(%#v, %#v) = %#v, want %#v", test.messageType, p, got, want)
		}
	}
}

func TestParseWebHook_BadMessageType(t *testing.T) {
	if _, err := ParseWebHook("bogus message type", []byte("{}")); err == nil {
		t.Fatal("ParseWebHook returned nil; wanted error")
	}
}

func TestDeliveryID(t *testing.T) {
	id := "8970a780-244e-11e7-91ca-da3aabcb9793"
	req, err := http.NewRequest("POST", "http://localhost", nil)
	if err != nil {
		t.Fatalf("DeliveryID: %v", err)
	}
	req.Header.Set("X-Github-Delivery", id)

	got := DeliveryID(req)
	if got != id {
		t.Errorf("DeliveryID(%#v) = %q, want %q", req, got, id)
	}
}

func TestWebHookType(t *testing.T) {
	want := "yo"
	req := &http.Request{
		Header: http.Header{eventTypeHeader: []string{want}},
	}
	if got := WebHookType(req); got != want {
		t.Errorf("WebHookType = %q, want %q", got, want)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"net/smtp"
	"strings"
)

func parseDomain(email string) string {
	coll := strings.Split(email, "@")
	if len(coll) == 2 {
		return coll[1]
	}
	return ""
}

// VerifyEmail checks an email by using SMTP Callback Verification method
//
// Reference:
// http://en.wikipedia.org/wiki/Callback_verification
func VerifyEmail(email string) (isValid bool, err error) {
	mx, err := net.LookupMX(parseDomain(email))
	if err != nil {
		return
	}
	c, err := smtp.Dial(mx[0].Host + ":25")
	if err != nil {
		return
	}
	defer c.Close()

	err = c.Hello("verify-email.org")
	if err != nil {
		return
	}
	err = c.Mail("check@verify-email.org")
	if err != nil {
		return
	}
	if err := c.Rcpt(email); err != nil {
		return false, nil
	} else {
		return true, nil
	}
}
<commit_msg>Quite smtp instead of Close<commit_after>package main

import (
	"net"
	"net/smtp"
	"strings"
)

func parseDomain(email string) string {
	coll := strings.Split(email, "@")
	if len(coll) == 2 {
		return coll[1]
	}
	return ""
}

// VerifyEmail checks an email by using SMTP Callback Verification method
//
// Reference:
// http://en.wikipedia.org/wiki/Callback_verification
func VerifyEmail(email string) (isValid bool, err error) {
	mx, err := net.LookupMX(parseDomain(email))
	if err != nil {
		return
	}
	c, err := smtp.Dial(mx[0].Host + ":25")
	if err != nil {
		return
	}

	err = c.Hello("verify-email.org")
	if err != nil {
		return
	}
	err = c.Mail("check@verify-email.org")
	if err != nil {
		return
	}
	if err := c.Rcpt(email); err == nil {
		isValid = true
	}

	err = c.Quit()
	return
}
<|endoftext|>


<commit_before>package scheduler

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/url"
	"time"

	"github.com/chrislusf/glow/driver/plan"
	"github.com/chrislusf/glow/driver/scheduler/market"
	"github.com/chrislusf/glow/resource"
	"github.com/chrislusf/glow/util"
)

// Requirement is TaskGroup
// Object is Agent's Location
func (s *Scheduler) Fetch(demands []market.Demand) {
	var request resource.AllocationRequest
	for _, d := range demands {
		demand := d.Requirement.(*plan.TaskGroup)
		request.Requests = append(request.Requests, resource.ComputeRequest{
			ComputeResource: resource.ComputeResource{
				CPUCount: 1,
				CPULevel: 1,
				MemoryMB: int64(s.option.TaskMemoryMB),
			},
			Inputs: s.findTaskGroupInputs(demand),
		})
	}

	result, err := Assign(s.Leader, &request)
	if err != nil {
		log.Printf("%s Failed to allocate: %v", s.Leader, err)
		time.Sleep(time.Millisecond * time.Duration(15000+rand.Int63n(5000)))
	} else {
		if len(result.Allocations) == 0 {
			log.Printf("%s Failed to allocate any server.", s.Leader)
			time.Sleep(time.Millisecond * time.Duration(2000+rand.Int63n(1000)))
		} else {
			log.Printf("%s allocated %d servers.", s.Leader, len(result.Allocations))
			for _, allocation := range result.Allocations {
				s.Market.AddSupply(market.Supply{
					Object: allocation,
				})
			}
		}
	}
}

func (s *Scheduler) findTaskGroupInputs(tg *plan.TaskGroup) (ret []resource.DataResource) {
	firstTask := tg.Tasks[0]
	for _, input := range firstTask.Inputs {
		dataLocation, found := s.shardLocator.GetShardLocation(s.option.ExecutableFileHash + "-" + input.Name())
		if !found {
			// log.Printf("Strange2: %s not allocated yet.", input.Name())
			continue
		}
		ret = append(ret, resource.DataResource{
			Location:   dataLocation,
			DataSizeMB: 1, // TODO: read previous run's size
		})
	}
	return
}

func Assign(leader string, request *resource.AllocationRequest) (*resource.AllocationResult, error) {
	values := make(url.Values)
	requestBlob, _ := json.Marshal(request)
	values.Add("request", string(requestBlob))
	jsonBlob, err := util.Post("http://"+leader+"/agent/assign", values)
	if err != nil {
		return nil, err
	}
	var ret resource.AllocationResult
	err = json.Unmarshal(jsonBlob, &ret)
	if err != nil {
		return nil, fmt.Errorf("/agent/assign result JSON unmarshal error:%v, json:%s", err, string(jsonBlob))
	}
	if ret.Error != "" {
		return nil, fmt.Errorf("/agent/assign error:%v", ret.Error)
	}
	return &ret, nil
}
<commit_msg>textual output adjustment<commit_after>package scheduler

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/url"
	"time"

	"github.com/chrislusf/glow/driver/plan"
	"github.com/chrislusf/glow/driver/scheduler/market"
	"github.com/chrislusf/glow/resource"
	"github.com/chrislusf/glow/util"
)

// Requirement is TaskGroup
// Object is Agent's Location
func (s *Scheduler) Fetch(demands []market.Demand) {
	var request resource.AllocationRequest
	for _, d := range demands {
		demand := d.Requirement.(*plan.TaskGroup)
		request.Requests = append(request.Requests, resource.ComputeRequest{
			ComputeResource: resource.ComputeResource{
				CPUCount: 1,
				CPULevel: 1,
				MemoryMB: int64(s.option.TaskMemoryMB),
			},
			Inputs: s.findTaskGroupInputs(demand),
		})
	}

	result, err := Assign(s.Leader, &request)
	if err != nil {
		log.Printf("%s Failed to allocate: %v", s.Leader, err)
		time.Sleep(time.Millisecond * time.Duration(15000+rand.Int63n(5000)))
	} else {
		if len(result.Allocations) == 0 {
			log.Printf("%s Failed to allocate any executor.", s.Leader)
			time.Sleep(time.Millisecond * time.Duration(2000+rand.Int63n(1000)))
		} else {
			log.Printf("%s allocated %d executors.", s.Leader, len(result.Allocations))
			for _, allocation := range result.Allocations {
				s.Market.AddSupply(market.Supply{
					Object: allocation,
				})
			}
		}
	}
}

func (s *Scheduler) findTaskGroupInputs(tg *plan.TaskGroup) (ret []resource.DataResource) {
	firstTask := tg.Tasks[0]
	for _, input := range firstTask.Inputs {
		dataLocation, found := s.shardLocator.GetShardLocation(s.option.ExecutableFileHash + "-" + input.Name())
		if !found {
			// log.Printf("Strange2: %s not allocated yet.", input.Name())
			continue
		}
		ret = append(ret, resource.DataResource{
			Location:   dataLocation,
			DataSizeMB: 1, // TODO: read previous run's size
		})
	}
	return
}

func Assign(leader string, request *resource.AllocationRequest) (*resource.AllocationResult, error) {
	values := make(url.Values)
	requestBlob, _ := json.Marshal(request)
	values.Add("request", string(requestBlob))
	jsonBlob, err := util.Post("http://"+leader+"/agent/assign", values)
	if err != nil {
		return nil, err
	}
	var ret resource.AllocationResult
	err = json.Unmarshal(jsonBlob, &ret)
	if err != nil {
		return nil, fmt.Errorf("/agent/assign result JSON unmarshal error:%v, json:%s", err, string(jsonBlob))
	}
	if ret.Error != "" {
		return nil, fmt.Errorf("/agent/assign error:%v", ret.Error)
	}
	return &ret, nil
}
<|endoftext|>


<commit_before>package gitmediaclient

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/github/git-media/gitmedia"
	"github.com/rubyist/tracerx"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

const (
	gitMediaType     = "application/vnd.git-media"
	gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
	gitMediaHeader   = "--git-media."
)

type linkMeta struct {
	Links map[string]*link `json:"_links,omitempty"`
}

type link struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

func Options(filehash string) (int, error) {
	oid := filepath.Base(filehash)
	_, err := os.Stat(filehash)
	if err != nil {
		return 0, err
	}

	tracerx.Printf("api_options: %s", oid)
	req, creds, err := clientRequest("OPTIONS", oid)
	if err != nil {
		return 0, err
	}

	resp, wErr := doRequest(req, creds)
	if wErr != nil {
		return 0, wErr
	}
	tracerx.Printf("api_options_status: %d", resp.StatusCode)

	return resp.StatusCode, nil
}

func Put(filehash, filename string, cb gitmedia.CopyCallback) error {
	if filename == "" {
		filename = filehash
	}

	oid := filepath.Base(filehash)
	file, err := os.Open(filehash)
	if err != nil {
		return err
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	fileSize := stat.Size()
	reader := &gitmedia.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Header.Set("Content-Type", gitMediaType)
	req.Header.Set("Accept", gitMediaMetaType)
	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	fmt.Printf("Sending %s\n", filename)

	tracerx.Printf("api_put: %s %s", oid, filename)
	resp, wErr := doRequest(req, creds)
	if wErr != nil {
		return wErr
	}
	tracerx.Printf("api_put_status: %d", resp.StatusCode)

	return nil
}

func ExternalPut(filehash, filename string, lm *linkMeta, cb gitmedia.CopyCallback) error {
	link, ok := lm.Links["upload"]
	if !ok {
		return gitmedia.Error(errors.New("No upload link provided"))
	}

	file, err := os.Open(filehash)
	if err != nil {
		return err
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}
	fileSize := stat.Size()
	reader := &gitmedia.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	req, err := http.NewRequest("PUT", link.Href, nil)
	if err != nil {
		return gitmedia.Error(err)
	}
	for h, v := range link.Header {
		req.Header.Set(h, v)
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	tracerx.Printf("external_put: %s %s", filepath.Base(filehash), req.URL)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return gitmedia.Error(err)
	}
	tracerx.Printf("external_put_status: %d", res.StatusCode)

	// Run the callback
	if cb, ok := lm.Links["callback"]; ok {
		oid := filepath.Base(filehash)
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return gitmedia.Error(err)
		}

		cbreq, err := http.NewRequest("POST", cb.Href, nil)
		if err != nil {
			return gitmedia.Error(err)
		}
		for h, v := range cb.Header {
			cbreq.Header.Set(h, v)
		}

		d := fmt.Sprintf(`{"oid":"%s", "size":%d, "status":%d, "body":"%s"}`, oid, fileSize, res.StatusCode, string(body))
		cbreq.Body = ioutil.NopCloser(bytes.NewBufferString(d))

		cbreq.Header.Set("Accept", gitMediaMetaType) // Should this come from the client

		tracerx.Printf("callback: %s %s", oid, cb.Href)
		cbres, err := http.DefaultClient.Do(cbreq)
		if err != nil {
			return gitmedia.Error(err)
		}
		tracerx.Printf("callback_status: %d", cbres.StatusCode)
	}

	return nil
}

func Post(filehash, filename string) (*linkMeta, int, error) {
	oid := filepath.Base(filehash)
	req, creds, err := clientRequest("POST", "")
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}

	file, err := os.Open(filehash)
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}
	fileSize := stat.Size()

	d := fmt.Sprintf(`{"oid":"%s", "size":%d}`, oid, fileSize)
	req.Body = ioutil.NopCloser(bytes.NewBufferString(d))

	req.Header.Set("Accept", gitMediaMetaType)

	tracerx.Printf("api_post: %s %s", oid, filename)
	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}
	tracerx.Printf("api_post_status: %d", res.StatusCode)

	if res.StatusCode == 201 {
		var lm linkMeta
		dec := json.NewDecoder(res.Body)
		err := dec.Decode(&lm)
		if err != nil {
			return nil, res.StatusCode, gitmedia.Error(err)
		}

		return &lm, res.StatusCode, nil
	}

	return nil, res.StatusCode, nil
}

func Get(filename string) (io.ReadCloser, int64, *gitmedia.WrappedError) {
	oid := filepath.Base(filename)
	req, creds, err := clientRequest("GET", oid)
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}

	req.Header.Set("Accept", gitMediaType)
	res, wErr := doRequest(req, creds)

	if wErr != nil {
		return nil, 0, wErr
	}

	contentType := res.Header.Get("Content-Type")
	if contentType == "" {
		wErr = gitmedia.Error(errors.New("Empty Content-Type"))
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	if ok, wErr := validateMediaHeader(contentType, res.Body); !ok {
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	return res.Body, res.ContentLength, nil
}

func validateMediaHeader(contentType string, reader io.Reader) (bool, *gitmedia.WrappedError) {
	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		return false, gitmedia.Errorf(err, "Invalid Media Type: %s", contentType)
	}

	if mediaType == gitMediaType {

		givenHeader, ok := params["header"]
		if !ok {
			return false, gitmedia.Error(fmt.Errorf("Missing Git Media header in %s", contentType))
		}

		fullGivenHeader := "--" + givenHeader + "\n"

		header := make([]byte, len(fullGivenHeader))
		_, err = io.ReadAtLeast(reader, header, len(fullGivenHeader))
		if err != nil {
			return false, gitmedia.Errorf(err, "Error reading response body.")
		}

		if string(header) != fullGivenHeader {
			return false, gitmedia.Error(fmt.Errorf("Invalid header: %s expected, got %s", fullGivenHeader, header))
		}
	}
	return true, nil
}

func doRequest(req *http.Request, creds Creds) (*http.Response, *gitmedia.WrappedError) {
	res, err := gitmedia.HttpClient().Do(req)

	var wErr *gitmedia.WrappedError

	if err == nil {
		if res.StatusCode > 299 {
			// An auth error should be 403.  Could be 404 also.
			if res.StatusCode < 405 {
				execCreds(creds, "reject")
			}

			apierr := &Error{}
			dec := json.NewDecoder(res.Body)
			if err := dec.Decode(apierr); err != nil {
				wErr = gitmedia.Errorf(err, "Error decoding JSON from response")
			} else {
				wErr = gitmedia.Errorf(apierr, "Invalid response: %d", res.StatusCode)
			}
		} else {
			execCreds(creds, "approve")
		}
	} else {
		wErr = gitmedia.Errorf(err, "Error sending HTTP request to %s", req.URL.String())
	}

	if wErr != nil {
		if res != nil {
			setErrorResponseContext(wErr, res)
		} else {
			setErrorRequestContext(wErr, req)
		}
	}

	return res, wErr
}

var hiddenHeaders = map[string]bool{
	"Authorization": true,
}

func setErrorRequestContext(err *gitmedia.WrappedError, req *http.Request) {
	err.Set("Endpoint", gitmedia.Config.Endpoint())
	err.Set("URL", fmt.Sprintf("%s %s", req.Method, req.URL.String()))
	setErrorHeaderContext(err, "Response", req.Header)
}

func setErrorResponseContext(err *gitmedia.WrappedError, res *http.Response) {
	err.Set("Status", res.Status)
	setErrorHeaderContext(err, "Request", res.Header)
	setErrorRequestContext(err, res.Request)
}

func setErrorHeaderContext(err *gitmedia.WrappedError, prefix string, head http.Header) {
	for key, _ := range head {
		contextKey := fmt.Sprintf("%s:%s", prefix, key)
		if _, skip := hiddenHeaders[key]; skip {
			err.Set(contextKey, "--")
		} else {
			err.Set(contextKey, head.Get(key))
		}
	}
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	req.Header.Set("User-Agent", gitmedia.UserAgent)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := gitmedia.Config
	u, _ := url.Parse(c.Endpoint())
	if strings.HasSuffix(u.Path, "/") {
		u.Path = fmt.Sprintf("%sobjects/%s", u.Path, oid)
	} else {
		u.Path = fmt.Sprintf("%s/objects/%s", u.Path, oid)
	}
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<commit_msg>アー アアアア アーアー<commit_after>package gitmediaclient

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/github/git-media/gitmedia"
	"github.com/rubyist/tracerx"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
)

const (
	gitMediaType     = "application/vnd.git-media"
	gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
	gitMediaHeader   = "--git-media."
)

type linkMeta struct {
	Links map[string]*link `json:"_links,omitempty"`
}

type link struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

func Options(filehash string) (int, error) {
	oid := filepath.Base(filehash)
	_, err := os.Stat(filehash)
	if err != nil {
		return 0, err
	}

	tracerx.Printf("api_options: %s", oid)
	req, creds, err := clientRequest("OPTIONS", oid)
	if err != nil {
		return 0, err
	}

	resp, wErr := doRequest(req, creds)
	if wErr != nil {
		return 0, wErr
	}
	tracerx.Printf("api_options_status: %d", resp.StatusCode)

	return resp.StatusCode, nil
}

func Put(filehash, filename string, cb gitmedia.CopyCallback) error {
	if filename == "" {
		filename = filehash
	}

	oid := filepath.Base(filehash)
	file, err := os.Open(filehash)
	if err != nil {
		return err
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	fileSize := stat.Size()
	reader := &gitmedia.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Header.Set("Content-Type", gitMediaType)
	req.Header.Set("Accept", gitMediaMetaType)
	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	fmt.Printf("Sending %s\n", filename)

	tracerx.Printf("api_put: %s %s", oid, filename)
	resp, wErr := doRequest(req, creds)
	if wErr != nil {
		return wErr
	}
	tracerx.Printf("api_put_status: %d", resp.StatusCode)

	return nil
}

func ExternalPut(filehash, filename string, lm *linkMeta, cb gitmedia.CopyCallback) error {
	link, ok := lm.Links["upload"]
	if !ok {
		return gitmedia.Error(errors.New("No upload link provided"))
	}

	file, err := os.Open(filehash)
	if err != nil {
		return err
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}
	fileSize := stat.Size()
	reader := &gitmedia.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	req, err := http.NewRequest("PUT", link.Href, nil)
	if err != nil {
		return gitmedia.Error(err)
	}
	for h, v := range link.Header {
		req.Header.Set(h, v)
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	tracerx.Printf("external_put: %s %s", filepath.Base(filehash), req.URL)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return gitmedia.Error(err)
	}
	tracerx.Printf("external_put_status: %d", res.StatusCode)

	// Run the callback
	if cb, ok := lm.Links["callback"]; ok {
		oid := filepath.Base(filehash)
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return gitmedia.Error(err)
		}

		cbreq, err := http.NewRequest("POST", cb.Href, nil)
		if err != nil {
			return gitmedia.Error(err)
		}
		for h, v := range cb.Header {
			cbreq.Header.Set(h, v)
		}

		d := fmt.Sprintf(`{"oid":"%s", "size":%d, "status":%d, "body":"%s"}`, oid, fileSize, res.StatusCode, string(body))
		cbreq.Body = ioutil.NopCloser(bytes.NewBufferString(d))

		cbreq.Header.Set("Accept", gitMediaMetaType) // Should this come from the client

		tracerx.Printf("callback: %s %s", oid, cb.Href)
		cbres, err := http.DefaultClient.Do(cbreq)
		if err != nil {
			return gitmedia.Error(err)
		}
		tracerx.Printf("callback_status: %d", cbres.StatusCode)
	}

	return nil
}

func Post(filehash, filename string) (*linkMeta, int, error) {
	oid := filepath.Base(filehash)
	req, creds, err := clientRequest("POST", "")
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}

	file, err := os.Open(filehash)
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}
	fileSize := stat.Size()

	d := fmt.Sprintf(`{"oid":"%s", "size":%d}`, oid, fileSize)
	req.Body = ioutil.NopCloser(bytes.NewBufferString(d))

	req.Header.Set("Accept", gitMediaMetaType)

	tracerx.Printf("api_post: %s %s", oid, filename)
	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}
	tracerx.Printf("api_post_status: %d", res.StatusCode)

	if res.StatusCode == 201 {
		var lm linkMeta
		dec := json.NewDecoder(res.Body)
		err := dec.Decode(&lm)
		if err != nil {
			return nil, res.StatusCode, gitmedia.Error(err)
		}

		return &lm, res.StatusCode, nil
	}

	return nil, res.StatusCode, nil
}

func Get(filename string) (io.ReadCloser, int64, *gitmedia.WrappedError) {
	oid := filepath.Base(filename)
	req, creds, err := clientRequest("GET", oid)
	if err != nil {
		return nil, 0, gitmedia.Error(err)
	}

	req.Header.Set("Accept", gitMediaType)
	res, wErr := doRequest(req, creds)

	if wErr != nil {
		return nil, 0, wErr
	}

	contentType := res.Header.Get("Content-Type")
	if contentType == "" {
		wErr = gitmedia.Error(errors.New("Empty Content-Type"))
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	if ok, wErr := validateMediaHeader(contentType, res.Body); !ok {
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	return res.Body, res.ContentLength, nil
}

func validateMediaHeader(contentType string, reader io.Reader) (bool, *gitmedia.WrappedError) {
	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		return false, gitmedia.Errorf(err, "Invalid Media Type: %s", contentType)
	}

	if mediaType == gitMediaType {

		givenHeader, ok := params["header"]
		if !ok {
			return false, gitmedia.Error(fmt.Errorf("Missing Git Media header in %s", contentType))
		}

		fullGivenHeader := "--" + givenHeader + "\n"

		header := make([]byte, len(fullGivenHeader))
		_, err = io.ReadAtLeast(reader, header, len(fullGivenHeader))
		if err != nil {
			return false, gitmedia.Errorf(err, "Error reading response body.")
		}

		if string(header) != fullGivenHeader {
			return false, gitmedia.Error(fmt.Errorf("Invalid header: %s expected, got %s", fullGivenHeader, header))
		}
	}
	return true, nil
}

func doRequest(req *http.Request, creds Creds) (*http.Response, *gitmedia.WrappedError) {
	res, err := gitmedia.HttpClient().Do(req)

	var wErr *gitmedia.WrappedError

	if err == nil {
		if res.StatusCode > 299 {
			// An auth error should be 403.  Could be 404 also.
			if res.StatusCode < 405 {
				execCreds(creds, "reject")

				apierr := &Error{}
				dec := json.NewDecoder(res.Body)
				if err := dec.Decode(apierr); err != nil {
					wErr = gitmedia.Errorf(err, "Error decoding JSON from response")
				} else {
					wErr = gitmedia.Errorf(apierr, "Invalid response: %d", res.StatusCode)
				}
			}
		} else {
			execCreds(creds, "approve")
		}
	} else {
		wErr = gitmedia.Errorf(err, "Error sending HTTP request to %s", req.URL.String())
	}

	if wErr != nil {
		if res != nil {
			setErrorResponseContext(wErr, res)
		} else {
			setErrorRequestContext(wErr, req)
		}
	}

	return res, wErr
}

var hiddenHeaders = map[string]bool{
	"Authorization": true,
}

func setErrorRequestContext(err *gitmedia.WrappedError, req *http.Request) {
	err.Set("Endpoint", gitmedia.Config.Endpoint())
	err.Set("URL", fmt.Sprintf("%s %s", req.Method, req.URL.String()))
	setErrorHeaderContext(err, "Response", req.Header)
}

func setErrorResponseContext(err *gitmedia.WrappedError, res *http.Response) {
	err.Set("Status", res.Status)
	setErrorHeaderContext(err, "Request", res.Header)
	setErrorRequestContext(err, res.Request)
}

func setErrorHeaderContext(err *gitmedia.WrappedError, prefix string, head http.Header) {
	for key, _ := range head {
		contextKey := fmt.Sprintf("%s:%s", prefix, key)
		if _, skip := hiddenHeaders[key]; skip {
			err.Set(contextKey, "--")
		} else {
			err.Set(contextKey, head.Get(key))
		}
	}
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	req.Header.Set("User-Agent", gitmedia.UserAgent)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := gitmedia.Config
	u, _ := url.Parse(c.Endpoint())
	u.Path = path.Join(u.Path, "objects", oid)
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/parnurzeal/gorequest"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

///////////////////////////// CLI ENTRYPOINT //////////////////////////////////

func LoadDefaultConfigOrExit(http *gorequest.SuperAgent) *Config {
	pth := defaultConfigPath()
	errout := []error{}

	_, err := os.Stat(pth)

	if os.IsNotExist(err) {
		errout = append(errout, errors.New("No config file existed at "+pth+". You need to `nelson login` before running other commands."))
	}

	x, parsed := readConfigFile(pth)

	if x != nil {
		errout = append(errout, errors.New("Unable to read configuration file at '"+pth+"'. Reported error was: "+x.Error()))
	}

	ve := parsed.Validate()

	if ve != nil {
		errout = append(errout, ve...) // TIM: wtf golang, ... means "expand these as vararg function application"
	}

	// if there are errors loading the config, assume its an expired
	// token and try to regenerate the configuration
	if errout != nil {
		// configuration file does not exist
		if err != nil {
			bailout(errout)
		}

		if len(ve) > 0 {
			// retry the login based on information we know
			x := attemptConfigRefresh(http, parsed)
			// if that didnt help, then bail out and report the issue to the user.
			if x != nil {
				errout = append(errout, x...)
				bailout(errout)
			}
			_, contents := readConfigFile(pth)
			return contents
		}
	}
	// if regular loading of the config worked, then
	// just go with that! #happypath
	return parsed
}

func attemptConfigRefresh(http *gorequest.SuperAgent, existing *Config) []error {
	fmt.Println("Attempted token refresh...")
	e, u := hostFromUri(existing.Endpoint)
	if e != nil {
		return []error{e}
	}
	return Login(http, os.Getenv("GITHUB_TOKEN"), u, false)
}

func bailout(errors []error) {
	fmt.Println("🚫")
	fmt.Println("Encountered an unexpected problem(s) loading the configuration file: ")
	PrintTerminalErrors(errors)
	os.Exit(1)
}

////////////////////////////// CONFIG YAML ///////////////////////////////////

type Config struct {
	Endpoint      string `yaml:endpoint`
	ConfigSession `yaml:"session"`
}

type ConfigSession struct {
	Token     string `yaml:"token"`
	ExpiresAt int64  `yaml:"expires_at"`
}

func (c *Config) GetAuthCookie() *http.Cookie {
	expire := time.Now().AddDate(0, 0, 1)
	cookie := &http.Cookie{
		Name:       "nelson.session",
		Value:      c.ConfigSession.Token,
		Path:       "/",
		Domain:     "nelson-beta.oncue.verizon.net",
		Expires:    expire,
		RawExpires: expire.Format(time.UnixDate),
		MaxAge:     86400,
		Secure:     true,
		HttpOnly:   false,
	}

	return cookie
}

func generateConfigYaml(s Session, url string) string {
	temp := &Config{
		Endpoint: url,
		ConfigSession: ConfigSession{
			Token:     s.SessionToken,
			ExpiresAt: s.ExpiresAt,
		},
	}

	d, err := yaml.Marshal(&temp)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	return "---\n" + string(d)
}

func parseConfigYaml(yamlAsBytes []byte) *Config {
	temp := &Config{}
	err := yaml.Unmarshal(yamlAsBytes, &temp)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	return temp
}

func (c *Config) Validate() []error {
	// check that the token has not expired
	errs := []error{}

	if c.ConfigSession.ExpiresAt <= currentTimeMillis() {
		errs = append(errs, errors.New("Your session has expired. Please 'nelson login' again to reactivate your session."))
	}
	return errs
}

/////////////////////////////// CONFIG I/O ////////////////////////////////////

func defaultConfigPath() string {
	targetDir := os.Getenv("HOME") + "/.nelson"
	os.Mkdir(targetDir, 0775)
	return targetDir + "/config.yml"
}

// returns Unit, no error handling. YOLO
func writeConfigFile(s Session, url string, configPath string) {
	yamlConfig := generateConfigYaml(s, url)

	err := ioutil.WriteFile(configPath, []byte(yamlConfig), 0755)
	if err != nil {
		panic(err)
	}
}

func readConfigFile(configPath string) (error, *Config) {
	b, err := ioutil.ReadFile(configPath)
	return err, parseConfigYaml(b) // TIM: parsing never fails, right? ;-)
}
<commit_msg>make function to check for environment github token before trying to refresh with it<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/parnurzeal/gorequest"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

///////////////////////////// CLI ENTRYPOINT //////////////////////////////////

func LoadDefaultConfigOrExit(http *gorequest.SuperAgent) *Config {
	pth := defaultConfigPath()
	errout := []error{}

	_, err := os.Stat(pth)

	if os.IsNotExist(err) {
		errout = append(errout, errors.New("No config file existed at "+pth+". You need to `nelson login` before running other commands."))
	}

	x, parsed := readConfigFile(pth)

	if x != nil {
		errout = append(errout, errors.New("Unable to read configuration file at '"+pth+"'. Reported error was: "+x.Error()))
	}

	ve := parsed.Validate()

	if ve != nil {
		errout = append(errout, ve...) // TIM: wtf golang, ... means "expand these as vararg function application"
	}

	// if there are errors loading the config, assume its an expired
	// token and try to regenerate the configuration
	if errout != nil {
		// configuration file does not exist
		if err != nil {
			bailout(errout)
		}

		if len(ve) > 0 {
			// retry the login based on information we know
			x := attemptConfigRefresh(http, parsed)
			// if that didnt help, then bail out and report the issue to the user.
			if x != nil {
				errout = append(errout, x...)
				bailout(errout)
			}
			_, contents := readConfigFile(pth)
			return contents
		}
	}
	// if regular loading of the config worked, then
	// just go with that! #happypath
	return parsed
}

func attemptConfigRefresh(http *gorequest.SuperAgent, existing *Config) []error {
	fmt.Println("Attempted token refresh...")
	e, u := hostFromUri(existing.Endpoint)
	if e != nil {
		return []error{e}
	}
	var ghToken string = os.Getenv("GITHUB_TOKEN")
	if len([]rune(ghToken)) != 0 {
		return Login(http, os.Getenv("GITHUB_TOKEN"), u, false)
	}
	return []error{e}
}

func bailout(errors []error) {
	fmt.Println("🚫")
	fmt.Println("Encountered an unexpected problem(s) loading the configuration file: ")
	PrintTerminalErrors(errors)
	os.Exit(1)
}

////////////////////////////// CONFIG YAML ///////////////////////////////////

type Config struct {
	Endpoint      string `yaml:endpoint`
	ConfigSession `yaml:"session"`
}

type ConfigSession struct {
	Token     string `yaml:"token"`
	ExpiresAt int64  `yaml:"expires_at"`
}

func (c *Config) GetAuthCookie() *http.Cookie {
	expire := time.Now().AddDate(0, 0, 1)
	cookie := &http.Cookie{
		Name:       "nelson.session",
		Value:      c.ConfigSession.Token,
		Path:       "/",
		Domain:     "nelson-beta.oncue.verizon.net",
		Expires:    expire,
		RawExpires: expire.Format(time.UnixDate),
		MaxAge:     86400,
		Secure:     true,
		HttpOnly:   false,
	}

	return cookie
}

func generateConfigYaml(s Session, url string) string {
	temp := &Config{
		Endpoint: url,
		ConfigSession: ConfigSession{
			Token:     s.SessionToken,
			ExpiresAt: s.ExpiresAt,
		},
	}

	d, err := yaml.Marshal(&temp)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	return "---\n" + string(d)
}

func parseConfigYaml(yamlAsBytes []byte) *Config {
	temp := &Config{}
	err := yaml.Unmarshal(yamlAsBytes, &temp)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	return temp
}

func (c *Config) Validate() []error {
	// check that the token has not expired
	errs := []error{}

	if c.ConfigSession.ExpiresAt <= currentTimeMillis() {
		errs = append(errs, errors.New("Your session has expired. Please 'nelson login' again to reactivate your session."))
	}
	return errs
}

/////////////////////////////// CONFIG I/O ////////////////////////////////////

func defaultConfigPath() string {
	targetDir := os.Getenv("HOME") + "/.nelson"
	os.Mkdir(targetDir, 0775)
	return targetDir + "/config.yml"
}

// returns Unit, no error handling. YOLO
func writeConfigFile(s Session, url string, configPath string) {
	yamlConfig := generateConfigYaml(s, url)

	err := ioutil.WriteFile(configPath, []byte(yamlConfig), 0755)
	if err != nil {
		panic(err)
	}
}

func readConfigFile(configPath string) (error, *Config) {
	b, err := ioutil.ReadFile(configPath)
	return err, parseConfigYaml(b) // TIM: parsing never fails, right? ;-)
}
<|endoftext|>


<commit_before>package gproject

import (
	"testing"
)

func togglClient() *ProjectClient {
	client := mockClient()
	ws, err := NewClient(client)
	if err != nil {
		panic(err)
	}
	return ws
}

func TestProjectCreate(t *testing.T) {
	tClient := togglClient()
	c := &Project{Name: "Very Big Company", WId: 777}
	nc, err := tClient.Create(c)
	if err != nil {
		t.Fatal(err)
	}

	if nc.Name != "An awesome project" {
		t.Fatal("!= An awesome project")
	}

	if nc.Id != 3 {
		t.Fatal("!= 3")
	}

	if nc.WId != 777 {
		t.Fatal("!= 777")
	}
}

func TestProjectUpdate(t *testing.T) {
	tClient := togglClient()
	c := &Project{Id: 1, Name: "new name", WId: 777}
	nc, err := tClient.Update(c)
	if err != nil {
		t.Fatal(err)
	}

	if nc.Name != "new name" {
		t.Fatal("!= new name")
	}
}

func TestProjectDelete(t *testing.T) {
	tClient := togglClient()
	c := &Project{Id: 1, Name: "new name", WId: 777}
	err := tClient.Delete(c.Id)
	if err != nil {
		t.Fatal(err)
	}
}

func TestProjectList(t *testing.T) {
	tClient := togglClient()
	clients, err := tClient.List()
	if err != nil {
		t.Fatal(err)
	}
	if len(clients) != 2 {
		t.Fatal("Workspace is not 2")
	}
	if clients[0].Id != 1 {
		t.Error("Workspace Id is not 1")
	}
	if clients[0].Name != "Id 1" {
		t.Error("Workspace name not Id ")
	}

	if clients[1].Id != 2 {
		t.Error("Workspace Id is not 2")
	}
	if clients[1].Name != "Id 2" {
		t.Error("Workspace name")
	}
}

func TestProjectGet(t *testing.T) {
	tClient := togglClient()

	client, err := tClient.Get(1)
	if err != nil {
		t.Fatal(err)
	}
	if client.Id != 1 {
		t.Error("!= 1")
	}

	if client.Name != "Id 1" {
		t.Error("!= Id 1:  " + client.Name)
	}
}

func BenchmarkClientTransport_Get(b *testing.B) {
	b.ReportAllocs()
	tClient := togglClient()
	for i := 0; i < b.N; i++ {
		tClient.Get(1)
	}
}
<commit_msg>adding bench for gproject<commit_after>package gproject

import (
	"testing"
)

func togglClient() *ProjectClient {
	client := mockClient()
	ws, err := NewClient(client)
	if err != nil {
		panic(err)
	}
	return ws
}

func TestProjectCreate(t *testing.T) {
	tClient := togglClient()
	c := &Project{Name: "Very Big Company", WId: 777}
	nc, err := tClient.Create(c)
	if err != nil {
		t.Fatal(err)
	}

	if nc.Name != "An awesome project" {
		t.Fatal("!= An awesome project")
	}

	if nc.Id != 3 {
		t.Fatal("!= 3")
	}

	if nc.WId != 777 {
		t.Fatal("!= 777")
	}
}

func TestProjectUpdate(t *testing.T) {
	tClient := togglClient()
	c := &Project{Id: 1, Name: "new name", WId: 777}
	nc, err := tClient.Update(c)
	if err != nil {
		t.Fatal(err)
	}

	if nc.Name != "new name" {
		t.Fatal("!= new name")
	}
}

func TestProjectDelete(t *testing.T) {
	tClient := togglClient()
	c := &Project{Id: 1, Name: "new name", WId: 777}
	err := tClient.Delete(c.Id)
	if err != nil {
		t.Fatal(err)
	}
}

func TestProjectList(t *testing.T) {
	tClient := togglClient()
	clients, err := tClient.List()
	if err != nil {
		t.Fatal(err)
	}
	if len(clients) != 2 {
		t.Fatal("Workspace is not 2")
	}
	if clients[0].Id != 1 {
		t.Error("Workspace Id is not 1")
	}
	if clients[0].Name != "Id 1" {
		t.Error("Workspace name not Id ")
	}

	if clients[1].Id != 2 {
		t.Error("Workspace Id is not 2")
	}
	if clients[1].Name != "Id 2" {
		t.Error("Workspace name")
	}
}

func TestProjectGet(t *testing.T) {
	tClient := togglClient()

	client, err := tClient.Get(1)
	if err != nil {
		t.Fatal(err)
	}
	if client.Id != 1 {
		t.Error("!= 1")
	}

	if client.Name != "Id 1" {
		t.Error("!= Id 1:  " + client.Name)
	}
}

func BenchmarkClientTransport_Get(b *testing.B) {
	b.ReportAllocs()
	tClient := togglClient()
	for i := 0; i < b.N; i++ {
		tClient.Get(1)
	}
}

func BenchmarkClientTransport_List(b *testing.B) {
	b.ReportAllocs()
	tClient := togglClient()
	for i := 0; i < b.N; i++ {
		tClient.List()
	}
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0
// Details: https://raw.githubusercontent.com/square/quotaservice/master/LICENSE

package config

import (
	"testing"

	pb "github.com/square/quotaservice/protos/config"
)

func defaultConfig() *pb.ServiceConfig {
	cfg := NewDefaultServiceConfig()

	ns := NewDefaultNamespaceConfig("testNamespace")
	cfg.Namespaces["testNamespace"] = ns

	bt := NewDefaultBucketConfig("testBucket")
	ns.Buckets["testBucket"] = bt

	return cfg
}

func TestGlobalCreateBucket(t *testing.T) {
	cfg := defaultConfig()
	bucket := NewDefaultBucketConfig("newBucket")

	err := CreateBucket(cfg, GlobalNamespace, bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.GlobalDefaultBucket != bucket {
		t.Error("newBucket was not created and added to config")
	}

	err = CreateBucket(cfg, GlobalNamespace, bucket)

	if err == nil {
		t.Error("CreateBucket was supposed to error on duplicate DefaultBucket")
	}
}

func TestCreateBucket(t *testing.T) {
	nilMapBucketsCfg := defaultConfig()
	nilMapBucketsCfg.Namespaces["testNamespace"].Buckets = nil
	bucket := NewDefaultBucketConfig("newBucket")
	err := CreateBucket(nilMapBucketsCfg, "testNamespace", bucket)

	if err != nil {
		t.Error("CreateBucket was supposed to create create a bucket in namespace with nil buckets map")
	}

	cfg := defaultConfig()
	bucket = NewDefaultBucketConfig("newBucket")

	err = CreateBucket(cfg, "nilNamespace", bucket)

	if err == nil {
		t.Error("CreateBucket was supposed to error on nonexistent namespace")
	}

	err = CreateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].Buckets["newBucket"] != bucket {
		t.Error("newBucket was not created and added to config")
	}

	bucket = NewDefaultBucketConfig("testBucket")
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err == nil {
		t.Error("CreateBucket was supposed to error on dup bucket")
	}

	bucket = NewDefaultBucketConfig(DefaultBucketName)
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DefaultBucket != bucket {
		t.Error("CreateBucket should have set DefaultBucket on namespace")
	}

	cfg.Namespaces["testNamespace"].DefaultBucket = bucket
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err == nil {
		t.Fatalf("CreateBucket should have errored on duplicate DefaultBucket")
	}

	bucket = NewDefaultBucketConfig(DynamicBucketTemplateName)
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DynamicBucketTemplate != bucket {
		t.Error("CreateBucket should have set DynamicBucketTemplate on namespace")
	}

	cfg.Namespaces["testNamespace"].DynamicBucketTemplate = bucket
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err == nil {
		t.Fatalf("CreateBucket should have errored on duplicate DynamicBucketTemplate")
	}
}

func TestGlobalUpdateBucket(t *testing.T) {
	cfg := defaultConfig()
	bucket := NewDefaultBucketConfig("newBucket")

	err := UpdateBucket(cfg, GlobalNamespace, bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.GlobalDefaultBucket != bucket {
		t.Error("newBucket was not created and added to config")
	}
}

func TestUpdateBucket(t *testing.T) {
	cfg := defaultConfig()
	bucket := NewDefaultBucketConfig("testBucket")

	err := UpdateBucket(cfg, "nilNamespace", bucket)

	if err == nil {
		t.Error("UpdateBucket was supposed to error on nonexistent namespace")
	}

	err = UpdateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].Buckets["testBucket"] != bucket {
		t.Error("testBucket was not updated")
	}

	bucket = NewDefaultBucketConfig(DefaultBucketName)
	err = UpdateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DefaultBucket != bucket {
		t.Error("CreateBucket should have set DefaultBucket on namespace")
	}

	bucket = NewDefaultBucketConfig(DynamicBucketTemplateName)
	err = UpdateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DynamicBucketTemplate != bucket {
		t.Error("UpdateBucket should have set DynamicBucketTemplate on namespace")
	}
}

func TestGlobalDeleteBucket(t *testing.T) {
	cfg := defaultConfig()
	cfg.GlobalDefaultBucket = NewDefaultBucketConfig("newBucket")

	err := DeleteBucket(cfg, GlobalNamespace, "")

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.GlobalDefaultBucket != nil {
		t.Error("global default bucket was not removed")
	}
}

func TestDeleteBucket(t *testing.T) {
	cfg := defaultConfig()

	err := DeleteBucket(cfg, "nilNamespace", "testBucket")

	if err == nil {
		t.Error("UpdateBucket was supposed to error on nonexistent namespace")
	}

	err = DeleteBucket(cfg, "testNamespace", "testBucket")

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].Buckets["testBucket"] != nil {
		t.Error("testBucket was not removed")
	}

	bucket := NewDefaultBucketConfig(DefaultBucketName)
	cfg.Namespaces["testNamespace"].DefaultBucket = bucket
	err = DeleteBucket(cfg, "testNamespace", DefaultBucketName)

	if err != nil {
		t.Fatalf("DeleteBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DefaultBucket != nil {
		t.Error("DeleteBucket should have removed DefaultBucket")
	}

	bucket = NewDefaultBucketConfig(DynamicBucketTemplateName)
	cfg.Namespaces["testNamespace"].DynamicBucketTemplate = bucket
	err = DeleteBucket(cfg, "testNamespace", DynamicBucketTemplateName)

	if err != nil {
		t.Fatalf("DeleteBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DynamicBucketTemplate != nil {
		t.Error("DeleteBucket should have removed DynamicBucketTemplate")
	}
}

func TestDeleteNamespace(t *testing.T) {
	cfg := defaultConfig()

	err := DeleteNamespace(cfg, "nonexistent")

	if err == nil {
		t.Error("DeleteNamespace did not error on nonexistent namespace")
	}

	err = DeleteNamespace(cfg, "testNamespace")

	if err != nil {
		t.Fatalf("DeleteNamespace errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"] != nil {
		t.Error("DeleteNamespace did not remove testNamespace")
	}
}

func TestCreateNamespace(t *testing.T) {
	cfg := defaultConfig()
	ns := NewDefaultNamespaceConfig("newNamespace")

	err := CreateNamespace(cfg, ns)

	if err != nil {
		t.Fatalf("CreateNamespace errored: %+v", err)
	}

	if cfg.Namespaces["newNamespace"] != ns {
		t.Error("CreateNamespace did not add newNamespace properly")
	}

	ns = NewDefaultNamespaceConfig("testNamespace")
	err = CreateNamespace(cfg, ns)

	if err == nil {
		t.Error("CreateNamespace did not error on duplicate namespace")
	}
}

func TestUpdateNamespace(t *testing.T) {
	cfg := defaultConfig()
	ns := NewDefaultNamespaceConfig("newNamespace")

	err := UpdateNamespace(cfg, ns)

	if err != nil {
		t.Fatalf("UpdateNamespace errored: %+v", err)
	}

	if cfg.Namespaces["newNamespace"] != ns {
		t.Error("UpdateNamespace did not add newNamespace properly")
	}

	ns = NewDefaultNamespaceConfig("testNamespace")
	err = UpdateNamespace(cfg, ns)

	if err != nil {
		t.Fatalf("UpdateNamespace errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"] != ns {
		t.Error("UpdateNamespace did not update testNamespace")
	}

	newCfg := &pb.ServiceConfig{}
	err = UpdateNamespace(newCfg, ns)

	if err != nil {
		t.Fatalf("UpdateNamespace errored: %+v", err)
	}

	if newCfg.Namespaces["testNamespace"] != ns {
		t.Error("UpdateNamespace did not update testNamespace")
	}
}
<commit_msg>Moving a test a separte test case<commit_after>// Licensed under the Apache License, Version 2.0
// Details: https://raw.githubusercontent.com/square/quotaservice/master/LICENSE

package config

import (
	"testing"

	pb "github.com/square/quotaservice/protos/config"
)

func defaultConfig() *pb.ServiceConfig {
	cfg := NewDefaultServiceConfig()

	ns := NewDefaultNamespaceConfig("testNamespace")
	cfg.Namespaces["testNamespace"] = ns

	bt := NewDefaultBucketConfig("testBucket")
	ns.Buckets["testBucket"] = bt

	return cfg
}

func TestGlobalCreateBucket(t *testing.T) {
	cfg := defaultConfig()
	bucket := NewDefaultBucketConfig("newBucket")

	err := CreateBucket(cfg, GlobalNamespace, bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.GlobalDefaultBucket != bucket {
		t.Error("newBucket was not created and added to config")
	}

	err = CreateBucket(cfg, GlobalNamespace, bucket)

	if err == nil {
		t.Error("CreateBucket was supposed to error on duplicate DefaultBucket")
	}
}

func TestCreateBucket(t *testing.T) {
	cfg := defaultConfig()
	bucket := NewDefaultBucketConfig("newBucket")

	err := CreateBucket(cfg, "nilNamespace", bucket)

	if err == nil {
		t.Error("CreateBucket was supposed to error on nonexistent namespace")
	}

	err = CreateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].Buckets["newBucket"] != bucket {
		t.Error("newBucket was not created and added to config")
	}

	bucket = NewDefaultBucketConfig("testBucket")
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err == nil {
		t.Error("CreateBucket was supposed to error on dup bucket")
	}

	bucket = NewDefaultBucketConfig(DefaultBucketName)
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DefaultBucket != bucket {
		t.Error("CreateBucket should have set DefaultBucket on namespace")
	}

	cfg.Namespaces["testNamespace"].DefaultBucket = bucket
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err == nil {
		t.Fatalf("CreateBucket should have errored on duplicate DefaultBucket")
	}

	bucket = NewDefaultBucketConfig(DynamicBucketTemplateName)
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DynamicBucketTemplate != bucket {
		t.Error("CreateBucket should have set DynamicBucketTemplate on namespace")
	}

	cfg.Namespaces["testNamespace"].DynamicBucketTemplate = bucket
	err = CreateBucket(cfg, "testNamespace", bucket)

	if err == nil {
		t.Fatalf("CreateBucket should have errored on duplicate DynamicBucketTemplate")
	}
}

func TestCreateBucketInConfigWithNilBucketsMap(t *testing.T) {
	nilMapBucketsCfg := defaultConfig()
	nilMapBucketsCfg.Namespaces["testNamespace"].Buckets = nil
	bucket := NewDefaultBucketConfig("newBucket")
	err := CreateBucket(nilMapBucketsCfg, "testNamespace", bucket)

	if err != nil {
		t.Error("CreateBucket was supposed to create create a bucket in namespace with nil buckets map")
	}
}

func TestGlobalUpdateBucket(t *testing.T) {
	cfg := defaultConfig()
	bucket := NewDefaultBucketConfig("newBucket")

	err := UpdateBucket(cfg, GlobalNamespace, bucket)

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.GlobalDefaultBucket != bucket {
		t.Error("newBucket was not created and added to config")
	}
}

func TestUpdateBucket(t *testing.T) {
	cfg := defaultConfig()
	bucket := NewDefaultBucketConfig("testBucket")

	err := UpdateBucket(cfg, "nilNamespace", bucket)

	if err == nil {
		t.Error("UpdateBucket was supposed to error on nonexistent namespace")
	}

	err = UpdateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].Buckets["testBucket"] != bucket {
		t.Error("testBucket was not updated")
	}

	bucket = NewDefaultBucketConfig(DefaultBucketName)
	err = UpdateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DefaultBucket != bucket {
		t.Error("CreateBucket should have set DefaultBucket on namespace")
	}

	bucket = NewDefaultBucketConfig(DynamicBucketTemplateName)
	err = UpdateBucket(cfg, "testNamespace", bucket)

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DynamicBucketTemplate != bucket {
		t.Error("UpdateBucket should have set DynamicBucketTemplate on namespace")
	}
}

func TestGlobalDeleteBucket(t *testing.T) {
	cfg := defaultConfig()
	cfg.GlobalDefaultBucket = NewDefaultBucketConfig("newBucket")

	err := DeleteBucket(cfg, GlobalNamespace, "")

	if err != nil {
		t.Fatalf("CreateBucket errored: %+v", err)
	}

	if cfg.GlobalDefaultBucket != nil {
		t.Error("global default bucket was not removed")
	}
}

func TestDeleteBucket(t *testing.T) {
	cfg := defaultConfig()

	err := DeleteBucket(cfg, "nilNamespace", "testBucket")

	if err == nil {
		t.Error("UpdateBucket was supposed to error on nonexistent namespace")
	}

	err = DeleteBucket(cfg, "testNamespace", "testBucket")

	if err != nil {
		t.Fatalf("UpdateBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].Buckets["testBucket"] != nil {
		t.Error("testBucket was not removed")
	}

	bucket := NewDefaultBucketConfig(DefaultBucketName)
	cfg.Namespaces["testNamespace"].DefaultBucket = bucket
	err = DeleteBucket(cfg, "testNamespace", DefaultBucketName)

	if err != nil {
		t.Fatalf("DeleteBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DefaultBucket != nil {
		t.Error("DeleteBucket should have removed DefaultBucket")
	}

	bucket = NewDefaultBucketConfig(DynamicBucketTemplateName)
	cfg.Namespaces["testNamespace"].DynamicBucketTemplate = bucket
	err = DeleteBucket(cfg, "testNamespace", DynamicBucketTemplateName)

	if err != nil {
		t.Fatalf("DeleteBucket errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"].DynamicBucketTemplate != nil {
		t.Error("DeleteBucket should have removed DynamicBucketTemplate")
	}
}

func TestDeleteNamespace(t *testing.T) {
	cfg := defaultConfig()

	err := DeleteNamespace(cfg, "nonexistent")

	if err == nil {
		t.Error("DeleteNamespace did not error on nonexistent namespace")
	}

	err = DeleteNamespace(cfg, "testNamespace")

	if err != nil {
		t.Fatalf("DeleteNamespace errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"] != nil {
		t.Error("DeleteNamespace did not remove testNamespace")
	}
}

func TestCreateNamespace(t *testing.T) {
	cfg := defaultConfig()
	ns := NewDefaultNamespaceConfig("newNamespace")

	err := CreateNamespace(cfg, ns)

	if err != nil {
		t.Fatalf("CreateNamespace errored: %+v", err)
	}

	if cfg.Namespaces["newNamespace"] != ns {
		t.Error("CreateNamespace did not add newNamespace properly")
	}

	ns = NewDefaultNamespaceConfig("testNamespace")
	err = CreateNamespace(cfg, ns)

	if err == nil {
		t.Error("CreateNamespace did not error on duplicate namespace")
	}
}

func TestUpdateNamespace(t *testing.T) {
	cfg := defaultConfig()
	ns := NewDefaultNamespaceConfig("newNamespace")

	err := UpdateNamespace(cfg, ns)

	if err != nil {
		t.Fatalf("UpdateNamespace errored: %+v", err)
	}

	if cfg.Namespaces["newNamespace"] != ns {
		t.Error("UpdateNamespace did not add newNamespace properly")
	}

	ns = NewDefaultNamespaceConfig("testNamespace")
	err = UpdateNamespace(cfg, ns)

	if err != nil {
		t.Fatalf("UpdateNamespace errored: %+v", err)
	}

	if cfg.Namespaces["testNamespace"] != ns {
		t.Error("UpdateNamespace did not update testNamespace")
	}

	newCfg := &pb.ServiceConfig{}
	err = UpdateNamespace(newCfg, ns)

	if err != nil {
		t.Fatalf("UpdateNamespace errored: %+v", err)
	}

	if newCfg.Namespaces["testNamespace"] != ns {
		t.Error("UpdateNamespace did not update testNamespace")
	}
}
<|endoftext|>