<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"container/list"
	"fmt"
	"os"

	"github.com/jacobsa/fuse/fsutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// A type that manages read and read/write leases for anonymous temporary files.
//
// Safe for concurrent access. Must be created with NewFileLeaser.
type FileLeaser struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	dir   string
	limit int64

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A lock that guards the mutable state in this struct, which must not be
	// held for any blocking operation.
	//
	// Lock ordering
	// -------------
	//
	// Define our strict partial order < as follows:
	//
	//  1. For any read/write lease W, W < leaser.
	//  2. For any read lease R, R < leaser.
	//  3. For any read/write lease W and read lease R, W < R.
	//
	// In other words: read/write before read before leaser, and never hold two
	// locks from the same category together.
	mu syncutil.InvariantMutex

	// The current estimated total size of outstanding read/write leases. This is
	// only an estimate because we can't synchronize its update with a call to
	// the wrapped file to e.g. write or truncate.
	readWriteOutstanding int64

	// All outstanding read leases, ordered by recency of use.
	//
	// INVARIANT: Each element is of type *readLease
	// INVARIANT: No element has been revoked.
	readLeases list.List

	// The sum of all outstanding read lease sizes.
	//
	// INVARIANT: Equal to the sum over readLeases sizes.
	// INVARIANT: 0 <= readOutstanding
	// INVARIANT: readOutstanding <= max(0, limit - readWriteOutstanding)
	readOutstanding int64

	// Index of read leases by pointer.
	//
	// INVARIANT: Is an index of exactly the elements of readLeases
	readLeasesIndex map[*readLease]*list.Element
}

// Create a new file leaser that uses the supplied directory for temporary
// files (before unlinking them) and attempts to keep usage in bytes below the
// given limit. If dir is empty, the system default will be used.
//
// Usage may exceed the given limit if there are read/write leases whose total
// size exceeds the limit, since such leases cannot be revoked.
func NewFileLeaser(
	dir string,
	limitBytes int64) (fl *FileLeaser) {
	fl = &FileLeaser{
		dir:             dir,
		limit:           limitBytes,
		readLeasesIndex: make(map[*readLease]*list.Element),
	}

	fl.mu = syncutil.NewInvariantMutex(fl.checkInvariants)

	return
}

// Create a new anonymous file, and return a read/write lease for it. The
// read/write lease will pin resources until rwl.Downgrade is called. It need
// not be called if the process is exiting.
func (fl *FileLeaser) NewFile() (rwl ReadWriteLease, err error) {
	// Create an anonymous file.
	f, err := fsutil.AnonymousFile(fl.dir)
	if err != nil {
		err = fmt.Errorf("AnonymousFile: %v", err)
		return
	}

	// Wrap a lease around it.
	rwl = newReadWriteLease(fl, 0, f)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func maxInt64(a int64, b int64) int64 {
	if a > b {
		return a
	}

	return b
}

// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) checkInvariants() {
	// INVARIANT: Each element is of type *readLease
	// INVARIANT: No element has been revoked.
	for e := fl.readLeases.Front(); e != nil; e = e.Next() {
		rl := e.Value.(*readLease)
		if rl.revoked() {
			panic("Found revoked read lease")
		}
	}

	// INVARIANT: Equal to the sum over readLeases sizes.
	var sum int64
	for e := fl.readLeases.Front(); e != nil; e = e.Next() {
		rl := e.Value.(*readLease)
		sum += rl.Size()
	}

	if fl.readOutstanding != sum {
		panic(fmt.Sprintf(
			"readOutstanding mismatch: %v vs. %v",
			fl.readOutstanding,
			sum))
	}

	// INVARIANT: 0 <= readOutstanding
	if !(0 <= fl.readOutstanding) {
		panic(fmt.Sprintf("Unexpected readOutstanding: %v", fl.readOutstanding))
	}

	// INVARIANT: readOutstanding <= max(0, limit - readWriteOutstanding)
	if !(fl.readOutstanding <= maxInt64(0, fl.limit-fl.readWriteOutstanding)) {
		panic(fmt.Sprintf(
			"Unexpected readOutstanding: %v. limit: %v, readWriteOutstanding: %v",
			fl.readOutstanding,
			fl.limit,
			fl.readWriteOutstanding))
	}

	// INVARIANT: Is an index of exactly the elements of readLeases
	if len(fl.readLeasesIndex) != fl.readLeases.Len() {
		panic(fmt.Sprintf(
			"readLeasesIndex length mismatch: %v vs. %v",
			len(fl.readLeasesIndex),
			fl.readLeases.Len()))
	}

	for e := fl.readLeases.Front(); e != nil; e = e.Next() {
		if fl.readLeasesIndex[e.Value.(*readLease)] != e {
			panic("Mismatch in readLeasesIndex")
		}
	}
}

// Add the supplied delta to the leaser's view of outstanding read/write lease
// bytes, then revoke read leases until we're under limit or we run out of
// leases to revoke.
//
// Called by readWriteLease while holding its lock.
//
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) addReadWriteByteDelta(delta int64) {
	// TODO(jacobsa): When evicting, repeatedly:
	// 1. Find least recently used read lease.
	// 2. Drop leaser lock.
	// 3. Acquire read lease lock.
	// 4. Reacquire leaser lock.
	// 5. If under limit now, drop both locks and return.
	// 6. If lease already evicted, drop its lock and go to #1.
	// 7. Evict lease, drop both locks. If still above limit, start over.
	panic("TODO")
}

// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) overLimit() bool {
	return fl.readOutstanding+fl.readWriteOutstanding > fl.limit
}

// An implementation detail of FileLeaser.evict.
//
// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) evictOne(rl *readLease) {
	// We must acquire the read lease's lock, which requires us to first drop
	// the leaser's lock, then reacquire it.
	fl.mu.Unlock()
	rl.Mu.Lock()
	defer rl.Mu.Unlock()
	fl.mu.Lock()

	// Now we have both locks, but the lease may have already been revoked. If
	// so, there's nothing to do and we should start the process over.
	if rl.revoked() {
		return
	}

	// Also no need to over-evict if someone has already done our job for us.
	if !fl.overLimit() {
		return
	}

	// Revoke the lease while holding its lock.
	fl.revoke(rl)
}

// Revoke read leases until we're under limit or we run out of things to revoke.
//
// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) evict() {
	for fl.overLimit() {
		// Do we have anything to revoke?
		lru := fl.readLeases.Back()
		if lru == nil {
			return
		}

		rl := lru.Value.(*readLease)
		fl.evictOne(rl)
	}
}

// Downgrade the supplied read/write lease, given its current size and the
// underlying file.
//
// Called by readWriteLease with its lock held.
//
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) downgrade(
	rwl *readWriteLease,
	size int64,
	file *os.File) (rl ReadLease) {
	// Create the read lease.
	rlTyped := newReadLease(size, fl, file)
	rl = rlTyped

	// Update the leaser's state, noting the new read lease and that the
	// read/write lease has gone away.
	fl.mu.Lock()
	defer fl.mu.Unlock()

	fl.readWriteOutstanding -= size
	fl.readOutstanding += size

	e := fl.readLeases.PushFront(rl)
	fl.readLeasesIndex[rlTyped] = e

	// Ensure that we're not now over capacity.
	fl.evict()

	return
}

// Upgrade the supplied read lease, given its size and the underlying file.
//
// Called by readLease with its lock held.
//
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) upgrade(
	rl *readLease,
	size int64,
	file *os.File) (rwl ReadWriteLease) {
	fl.mu.Lock()
	defer fl.mu.Unlock()

	// Update leaser state.
	fl.readWriteOutstanding += size
	fl.readOutstanding -= size

	e := fl.readLeasesIndex[rl]
	delete(fl.readLeasesIndex, rl)
	fl.readLeases.Remove(e)

	// Crearte the read/write lease, telling it that we already know its initial
	// size.
	rwl = newReadWriteLease(fl, size, file)

	return
}

// Forcibly revoke the supplied read lease.
//
// LOCKS_REQUIRED(rl, fl.mu)
func (fl *FileLeaser) revoke(rl *readLease) {
	panic("TODO")
}

// Called by the read lease when the user wants to manually revoke it.
//
// LOCKS_REQUIRED(rl)
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) revokeVoluntarily(rl *readLease) {
	// TODO(jacobsa): Acquire file leaser lock, update file leaser state, call
	// rl.destroy. Later we can factor all but acquiring the lock out into a
	// separate helper that is shared by the revoke-for-capacity logic, which
	// will already have the lock.
	panic("TODO")
}
<commit_msg>FileLeaser.addReadWriteByteDelta<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"container/list"
	"fmt"
	"os"

	"github.com/jacobsa/fuse/fsutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// A type that manages read and read/write leases for anonymous temporary files.
//
// Safe for concurrent access. Must be created with NewFileLeaser.
type FileLeaser struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	dir   string
	limit int64

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A lock that guards the mutable state in this struct, which must not be
	// held for any blocking operation.
	//
	// Lock ordering
	// -------------
	//
	// Define our strict partial order < as follows:
	//
	//  1. For any read/write lease W, W < leaser.
	//  2. For any read lease R, R < leaser.
	//  3. For any read/write lease W and read lease R, W < R.
	//
	// In other words: read/write before read before leaser, and never hold two
	// locks from the same category together.
	mu syncutil.InvariantMutex

	// The current estimated total size of outstanding read/write leases. This is
	// only an estimate because we can't synchronize its update with a call to
	// the wrapped file to e.g. write or truncate.
	readWriteOutstanding int64

	// All outstanding read leases, ordered by recency of use.
	//
	// INVARIANT: Each element is of type *readLease
	// INVARIANT: No element has been revoked.
	readLeases list.List

	// The sum of all outstanding read lease sizes.
	//
	// INVARIANT: Equal to the sum over readLeases sizes.
	// INVARIANT: 0 <= readOutstanding
	// INVARIANT: readOutstanding <= max(0, limit - readWriteOutstanding)
	readOutstanding int64

	// Index of read leases by pointer.
	//
	// INVARIANT: Is an index of exactly the elements of readLeases
	readLeasesIndex map[*readLease]*list.Element
}

// Create a new file leaser that uses the supplied directory for temporary
// files (before unlinking them) and attempts to keep usage in bytes below the
// given limit. If dir is empty, the system default will be used.
//
// Usage may exceed the given limit if there are read/write leases whose total
// size exceeds the limit, since such leases cannot be revoked.
func NewFileLeaser(
	dir string,
	limitBytes int64) (fl *FileLeaser) {
	fl = &FileLeaser{
		dir:             dir,
		limit:           limitBytes,
		readLeasesIndex: make(map[*readLease]*list.Element),
	}

	fl.mu = syncutil.NewInvariantMutex(fl.checkInvariants)

	return
}

// Create a new anonymous file, and return a read/write lease for it. The
// read/write lease will pin resources until rwl.Downgrade is called. It need
// not be called if the process is exiting.
func (fl *FileLeaser) NewFile() (rwl ReadWriteLease, err error) {
	// Create an anonymous file.
	f, err := fsutil.AnonymousFile(fl.dir)
	if err != nil {
		err = fmt.Errorf("AnonymousFile: %v", err)
		return
	}

	// Wrap a lease around it.
	rwl = newReadWriteLease(fl, 0, f)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func maxInt64(a int64, b int64) int64 {
	if a > b {
		return a
	}

	return b
}

// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) checkInvariants() {
	// INVARIANT: Each element is of type *readLease
	// INVARIANT: No element has been revoked.
	for e := fl.readLeases.Front(); e != nil; e = e.Next() {
		rl := e.Value.(*readLease)
		if rl.revoked() {
			panic("Found revoked read lease")
		}
	}

	// INVARIANT: Equal to the sum over readLeases sizes.
	var sum int64
	for e := fl.readLeases.Front(); e != nil; e = e.Next() {
		rl := e.Value.(*readLease)
		sum += rl.Size()
	}

	if fl.readOutstanding != sum {
		panic(fmt.Sprintf(
			"readOutstanding mismatch: %v vs. %v",
			fl.readOutstanding,
			sum))
	}

	// INVARIANT: 0 <= readOutstanding
	if !(0 <= fl.readOutstanding) {
		panic(fmt.Sprintf("Unexpected readOutstanding: %v", fl.readOutstanding))
	}

	// INVARIANT: readOutstanding <= max(0, limit - readWriteOutstanding)
	if !(fl.readOutstanding <= maxInt64(0, fl.limit-fl.readWriteOutstanding)) {
		panic(fmt.Sprintf(
			"Unexpected readOutstanding: %v. limit: %v, readWriteOutstanding: %v",
			fl.readOutstanding,
			fl.limit,
			fl.readWriteOutstanding))
	}

	// INVARIANT: Is an index of exactly the elements of readLeases
	if len(fl.readLeasesIndex) != fl.readLeases.Len() {
		panic(fmt.Sprintf(
			"readLeasesIndex length mismatch: %v vs. %v",
			len(fl.readLeasesIndex),
			fl.readLeases.Len()))
	}

	for e := fl.readLeases.Front(); e != nil; e = e.Next() {
		if fl.readLeasesIndex[e.Value.(*readLease)] != e {
			panic("Mismatch in readLeasesIndex")
		}
	}
}

// Add the supplied delta to the leaser's view of outstanding read/write lease
// bytes, then revoke read leases until we're under limit or we run out of
// leases to revoke.
//
// Called by readWriteLease while holding its lock.
//
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) addReadWriteByteDelta(delta int64) {
	fl.readWriteOutstanding += delta
	fl.evict()
}

// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) overLimit() bool {
	return fl.readOutstanding+fl.readWriteOutstanding > fl.limit
}

// An implementation detail of FileLeaser.evict.
//
// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) evictOne(rl *readLease) {
	// We must acquire the read lease's lock, which requires us to first drop
	// the leaser's lock, then reacquire it.
	fl.mu.Unlock()
	rl.Mu.Lock()
	defer rl.Mu.Unlock()
	fl.mu.Lock()

	// Now we have both locks, but the lease may have already been revoked. If
	// so, there's nothing to do and we should start the process over.
	if rl.revoked() {
		return
	}

	// Also no need to over-evict if someone has already done our job for us.
	if !fl.overLimit() {
		return
	}

	// Revoke the lease while holding its lock.
	fl.revoke(rl)
}

// Revoke read leases until we're under limit or we run out of things to revoke.
//
// LOCKS_REQUIRED(fl.mu)
func (fl *FileLeaser) evict() {
	for fl.overLimit() {
		// Do we have anything to revoke?
		lru := fl.readLeases.Back()
		if lru == nil {
			return
		}

		rl := lru.Value.(*readLease)
		fl.evictOne(rl)
	}
}

// Downgrade the supplied read/write lease, given its current size and the
// underlying file.
//
// Called by readWriteLease with its lock held.
//
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) downgrade(
	rwl *readWriteLease,
	size int64,
	file *os.File) (rl ReadLease) {
	// Create the read lease.
	rlTyped := newReadLease(size, fl, file)
	rl = rlTyped

	// Update the leaser's state, noting the new read lease and that the
	// read/write lease has gone away.
	fl.mu.Lock()
	defer fl.mu.Unlock()

	fl.readWriteOutstanding -= size
	fl.readOutstanding += size

	e := fl.readLeases.PushFront(rl)
	fl.readLeasesIndex[rlTyped] = e

	// Ensure that we're not now over capacity.
	fl.evict()

	return
}

// Upgrade the supplied read lease, given its size and the underlying file.
//
// Called by readLease with its lock held.
//
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) upgrade(
	rl *readLease,
	size int64,
	file *os.File) (rwl ReadWriteLease) {
	fl.mu.Lock()
	defer fl.mu.Unlock()

	// Update leaser state.
	fl.readWriteOutstanding += size
	fl.readOutstanding -= size

	e := fl.readLeasesIndex[rl]
	delete(fl.readLeasesIndex, rl)
	fl.readLeases.Remove(e)

	// Crearte the read/write lease, telling it that we already know its initial
	// size.
	rwl = newReadWriteLease(fl, size, file)

	return
}

// Forcibly revoke the supplied read lease.
//
// LOCKS_REQUIRED(rl, fl.mu)
func (fl *FileLeaser) revoke(rl *readLease) {
	panic("TODO")
}

// Called by the read lease when the user wants to manually revoke it.
//
// LOCKS_REQUIRED(rl)
// LOCKS_EXCLUDED(fl.mu)
func (fl *FileLeaser) revokeVoluntarily(rl *readLease) {
	// TODO(jacobsa): Acquire file leaser lock, update file leaser state, call
	// rl.destroy. Later we can factor all but acquiring the lock out into a
	// separate helper that is shared by the revoke-for-capacity logic, which
	// will already have the lock.
	panic("TODO")
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Mute Communications Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package client

import (
	"errors"
	"sync"
)

type errorTranslation struct {
	err   error
	fatal bool
}

var errorTranslateMap map[string]errorTranslation
var errorTranslateMutex *sync.Mutex

func registerTranslateError(errorString string, fatal bool) error {
	if errorTranslateMap == nil {
		errorTranslateMap = make(map[string]errorTranslation)
		errorTranslateMutex = new(sync.Mutex)
	}
	e := errorTranslation{
		err:   errors.New(errorString),
		fatal: fatal,
	}

	errorTranslateMap[errorString] = e
	return e.err
}

func lookupError(inputError error) (translated, fatal bool, err error) {
	errorTranslateMutex.Lock()
	defer errorTranslateMutex.Unlock()
	str := string(inputError.Error())
	if e, ok := errorTranslateMap[str]; ok {
		return true, e.fatal, e.err
	}
	return false, false, inputError
}

func translateError(inputError error) error {
	_, _, err := lookupError(inputError)
	return err
}

var (
	// Errors produced by the client

	// ErrRetry is returned on recoverable errors
	ErrRetry = errors.New("client: Retry")
	// ErrOffline is returned if the client is offline
	ErrOffline = errors.New("client: Offline")
	// ErrFinal is returned on final errors that cannot continue
	ErrFinal = errors.New("client: Final error")
	// ErrFatal is returned on fatal errors produced by the client implementation itself
	ErrFatal = errors.New("client: Fatal client error")
	// ErrNeedReissue is returned if a non-renewable token was issued by the walletserver but not owner was specified
	ErrNeedReissue = errors.New("client: Token needs reissue to owner")
	// ErrNotMine is returned if a token is not under our control
	ErrNotMine = errors.New("client: Not my token")
	// ErrLocked is returned if a token has been locked
	ErrLocked = errors.New("client: Token in use")
	// ErrExpireToken is returned if a token has expired
	ErrExpireToken = errors.New("client: Token expired")
	// ErrSignatureToken is returned if the token has a bad signature
	ErrSignatureToken = errors.New("client: Bad signature")
	// ErrOwnerToken is returned if a token has an unexpected owner
	ErrOwnerToken = errors.New("client: Unexpected owner")
	// ErrUsageToken is returned if a token has an unexpected usage
	ErrUsageToken = errors.New("client: Unexpected usage")
	// ErrTokenKnown is returned if a received token is already known
	ErrTokenKnown = errors.New("client: Token is already known")
	// ErrNoToken is returned if no token could be fetched from wallet storage
	ErrNoToken = errors.New("client: No token in wallet")
)

var (
	// Errors from wallet server:

	// ErrWalletServer is returned if the server experienced a problem that cannot be exposed to the client
	ErrWalletServer = registerTranslateError("walletserver: Server error", false)
	// ErrNoUser is returned if the user could not be found
	ErrNoUser = registerTranslateError("walletserver: No such user", true)
	// ErrBadToken is returned if the user's authtoken could not be verified
	ErrBadToken = registerTranslateError("walletserver: Bad token", true)
	// ErrInsufficientFunds is returned if the user's funds are exhausted
	ErrInsufficientFunds = registerTranslateError("walletserver: Insufficient funds", true)

	//
	// Errors from keylookup server:

	// ErrKeyLookupServer is returned if there was an error in the lookup server
	ErrKeyLookupServer = registerTranslateError("keylookup: Server error", false)
	// ErrNotFound is returned if the key could not be found
	ErrNotFound = registerTranslateError("keylookup: Key not found", true)

	//
	// Errors from issuer server:

	// ErrUsageMismatch is returned if the usage of the parameter publickey and the token publickey do not match
	ErrUsageMismatch = registerTranslateError("issuer: Parameter and Token usage mismatch", true)
	// ErrParamsExpired is returned if the parameters given are not in line with any available private key
	ErrParamsExpired = registerTranslateError("issuer: Parameters have expired", true)
	// ErrBadCallType is returned if a wrong calltype is present in a packet
	ErrBadCallType = registerTranslateError("issuer: Wrong calltype", true)
	// ErrIssuerServer is returned on server error
	ErrIssuerServer = registerTranslateError("issuer: Server errror", false)
	// ErrTokenDoubleSpend is returned if the token has already been spent
	ErrTokenDoubleSpend = registerTranslateError("issuer: Token double spend", true)
	// ErrParamDoubleSpend is returned if a parameter has already been spent
	ErrParamDoubleSpend = registerTranslateError("issuer: Parameter double spend", true)
	// ErrBadPacket is returned if packet contents cannot be unmarshalled
	ErrBadPacket = registerTranslateError("issuer: Bad package", true)
	// ErrOwnerSignature is returned if an owner signature could not be verified
	ErrOwnerSignature = registerTranslateError("issuer: Owner signature verification failed", true)
	// ErrWrongIssuer is returned if a token is presented to the wrong issuer
	ErrWrongIssuer = registerTranslateError("issuer: Wrong issuer", true)
	// ErrExpire is returned if a token has already expired
	ErrExpire = registerTranslateError("issuer: Token expired", true)
	// ErrBadUsage is returned if a key is presented with the wrong usage setting
	ErrBadUsage = registerTranslateError("issuer: Bad usage", true)
	// ErrBadIssuer is returned if a key is presented with the wrong issuer
	ErrBadIssuer = registerTranslateError("issuer: Bad issuer", true)
	// ErrBadSignature is returned if a signature could not be verified
	ErrBadSignature = registerTranslateError("issuer: Bad signature in token", true)
	// ErrParameterMixed is returned if the parameter set contains mixed keys
	ErrParameterMixed = registerTranslateError("issuer: Parameters were mixed", true)
)
<commit_msg>make error message more specific<commit_after>// Copyright (c) 2015 Mute Communications Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package client

import (
	"errors"
	"sync"
)

type errorTranslation struct {
	err   error
	fatal bool
}

var errorTranslateMap map[string]errorTranslation
var errorTranslateMutex *sync.Mutex

func registerTranslateError(errorString string, fatal bool) error {
	if errorTranslateMap == nil {
		errorTranslateMap = make(map[string]errorTranslation)
		errorTranslateMutex = new(sync.Mutex)
	}
	e := errorTranslation{
		err:   errors.New(errorString),
		fatal: fatal,
	}

	errorTranslateMap[errorString] = e
	return e.err
}

func lookupError(inputError error) (translated, fatal bool, err error) {
	errorTranslateMutex.Lock()
	defer errorTranslateMutex.Unlock()
	str := string(inputError.Error())
	if e, ok := errorTranslateMap[str]; ok {
		return true, e.fatal, e.err
	}
	return false, false, inputError
}

func translateError(inputError error) error {
	_, _, err := lookupError(inputError)
	return err
}

var (
	// Errors produced by the client

	// ErrRetry is returned on recoverable errors
	ErrRetry = errors.New("client: Retry")
	// ErrOffline is returned if the client is offline
	ErrOffline = errors.New("client: Offline")
	// ErrFinal is returned on final errors that cannot continue
	ErrFinal = errors.New("client: Final serviceguard error")
	// ErrFatal is returned on fatal errors produced by the client implementation itself
	ErrFatal = errors.New("client: Fatal client error")
	// ErrNeedReissue is returned if a non-renewable token was issued by the walletserver but not owner was specified
	ErrNeedReissue = errors.New("client: Token needs reissue to owner")
	// ErrNotMine is returned if a token is not under our control
	ErrNotMine = errors.New("client: Not my token")
	// ErrLocked is returned if a token has been locked
	ErrLocked = errors.New("client: Token in use")
	// ErrExpireToken is returned if a token has expired
	ErrExpireToken = errors.New("client: Token expired")
	// ErrSignatureToken is returned if the token has a bad signature
	ErrSignatureToken = errors.New("client: Bad signature")
	// ErrOwnerToken is returned if a token has an unexpected owner
	ErrOwnerToken = errors.New("client: Unexpected owner")
	// ErrUsageToken is returned if a token has an unexpected usage
	ErrUsageToken = errors.New("client: Unexpected usage")
	// ErrTokenKnown is returned if a received token is already known
	ErrTokenKnown = errors.New("client: Token is already known")
	// ErrNoToken is returned if no token could be fetched from wallet storage
	ErrNoToken = errors.New("client: No token in wallet")
)

var (
	// Errors from wallet server:

	// ErrWalletServer is returned if the server experienced a problem that cannot be exposed to the client
	ErrWalletServer = registerTranslateError("walletserver: Server error", false)
	// ErrNoUser is returned if the user could not be found
	ErrNoUser = registerTranslateError("walletserver: No such user", true)
	// ErrBadToken is returned if the user's authtoken could not be verified
	ErrBadToken = registerTranslateError("walletserver: Bad token", true)
	// ErrInsufficientFunds is returned if the user's funds are exhausted
	ErrInsufficientFunds = registerTranslateError("walletserver: Insufficient funds", true)

	//
	// Errors from keylookup server:

	// ErrKeyLookupServer is returned if there was an error in the lookup server
	ErrKeyLookupServer = registerTranslateError("keylookup: Server error", false)
	// ErrNotFound is returned if the key could not be found
	ErrNotFound = registerTranslateError("keylookup: Key not found", true)

	//
	// Errors from issuer server:

	// ErrUsageMismatch is returned if the usage of the parameter publickey and the token publickey do not match
	ErrUsageMismatch = registerTranslateError("issuer: Parameter and Token usage mismatch", true)
	// ErrParamsExpired is returned if the parameters given are not in line with any available private key
	ErrParamsExpired = registerTranslateError("issuer: Parameters have expired", true)
	// ErrBadCallType is returned if a wrong calltype is present in a packet
	ErrBadCallType = registerTranslateError("issuer: Wrong calltype", true)
	// ErrIssuerServer is returned on server error
	ErrIssuerServer = registerTranslateError("issuer: Server errror", false)
	// ErrTokenDoubleSpend is returned if the token has already been spent
	ErrTokenDoubleSpend = registerTranslateError("issuer: Token double spend", true)
	// ErrParamDoubleSpend is returned if a parameter has already been spent
	ErrParamDoubleSpend = registerTranslateError("issuer: Parameter double spend", true)
	// ErrBadPacket is returned if packet contents cannot be unmarshalled
	ErrBadPacket = registerTranslateError("issuer: Bad package", true)
	// ErrOwnerSignature is returned if an owner signature could not be verified
	ErrOwnerSignature = registerTranslateError("issuer: Owner signature verification failed", true)
	// ErrWrongIssuer is returned if a token is presented to the wrong issuer
	ErrWrongIssuer = registerTranslateError("issuer: Wrong issuer", true)
	// ErrExpire is returned if a token has already expired
	ErrExpire = registerTranslateError("issuer: Token expired", true)
	// ErrBadUsage is returned if a key is presented with the wrong usage setting
	ErrBadUsage = registerTranslateError("issuer: Bad usage", true)
	// ErrBadIssuer is returned if a key is presented with the wrong issuer
	ErrBadIssuer = registerTranslateError("issuer: Bad issuer", true)
	// ErrBadSignature is returned if a signature could not be verified
	ErrBadSignature = registerTranslateError("issuer: Bad signature in token", true)
	// ErrParameterMixed is returned if the parameter set contains mixed keys
	ErrParameterMixed = registerTranslateError("issuer: Parameters were mixed", true)
)
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import(
	`bufio`
	`io`
	`fmt`
	`os`
	`strconv`
	`strings`
)

// Brain-damaged universal chess interface (UCI) protocol as described at
// http://wbec-ridderkerk.nl/html/UCIProtocol.html
func (e *Engine) Uci() *Engine {
	var game *Game
	var position *Position

	// "uci" command handler.
	doUci := func(args []string) {
		fmt.Println(`Donna v1.0.0 Copyright (c) 2014 by Michael Dvorkin. All Rights Reserved.`)
		fmt.Println(`id author Michael Dvorkin`)
		fmt.Println(`uciok`)
	}

	// "ucinewgame" command handler.
	doUciNewGame := func(args []string) {
		game, position = nil, nil
	}

	// "isready" command handler.
	doIsReady := func(args []string) {
		fmt.Println(`readyok`)
	}

	// "position [startpos | fen ] [ moves ... ]" command handler.
	doPosition := func(args []string) {
		fmt.Printf("%q\n", args)

		// Make sure we've started the game since "ucinewgame" is optional.
		if game == nil || position == nil {
			game = NewGame()
			position = game.Start()
		}

		switch args[0] {
		case `startpos`:
			args = args[1:]
			position = NewInitialPosition(game)
		case `fen`:
			fen := []string{}
			for _, token := range args[1:] {
				args = args[1:] // Shift the token.
				if token == `moves` {
					break
				}
				fen = append(fen, token)
			}
			fmt.Printf("fen: %s\n", strings.Join(fen, ` `))
			position = NewPositionFromFEN(game, strings.Join(fen, ` `))
		default: return
		}

		fmt.Printf("args: %q\n%s\n", args, position)
		if position != nil && len(args) > 0 && args[0] == `moves` {
			for _, move := range args[1:] {
				args = args[1:] // Shift the move.
				position = position.MakeMove(NewMoveFromNotation(position, move))
			}
		}
		fmt.Printf("%s\n", position)
	}

	// "go [[wtime winc | btime binc ] movestogo] | depth | movetime"
	doGo := func(args []string) {
		fmt.Printf("%q\n", args)
		assign := func(key, value string) {
			fmt.Printf("assign(key `%s` value `%s`)\n", key, value)
			if n, err := strconv.Atoi(value); err == nil {
				e.Set(key, n)
			}
		}
		for i, token := range args {
			fmt.Printf("\t%d len %d Token [%v]\n", i, len(args), token)
			// Boolen "infinite" and "ponder" commands have no arguments, while "depth",
			// "nodes" etc. come with numeric argument.
			if token == `infinite` || token == `ponder` {
				e.Set(token, true)
			} else if len(args) > i+1 {
				switch token {
				case `depth`, `nodes`, `movetime`, `movestogo`:
					assign(token, args[i+1])
				case `wtime`:
					if position.color == White {
						assign(`time`, args[i+1])
					}
				case `btime`:
					if position.color == Black {
						assign(`time`, args[i+1])
					}
				case `winc`:
					if position.color == White {
						assign(`timeinc`, args[i+1])
					}
				case `binc`:
					if position.color == Black {
						assign(`timeinc`, args[i+1])
					}
				}
			}
		}
	}

	// Stop calculating as soon as possible.
	doStop := func(args []string) {
		e.clock.halt = true
	}

	var commands = map[string]func([]string){
		`isready`: doIsReady,
		`uci`: doUci,
		`ucinewgame`: doUciNewGame,
		`position`: doPosition,
		`go`: doGo,
		`stop`: doStop,
	}

 	bio := bufio.NewReader(os.Stdin)
	for {
		command, err := bio.ReadString('\n')
		if err != io.EOF && len(command) > 0 {
			args := strings.Split(command[:len(command)-1], ` `)
			if args[0] == `quit` {
				break
			}
			if handler, ok := commands[args[0]]; ok {
				handler(args[1:])
			}
		}
	}
	return e
}
<commit_msg>More straightforward UCI go command parsing<commit_after>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import(
	`bufio`
	`io`
	`fmt`
	`os`
	`strconv`
	`strings`
)

// Brain-damaged universal chess interface (UCI) protocol as described at
// http://wbec-ridderkerk.nl/html/UCIProtocol.html
func (e *Engine) Uci() *Engine {
	var game *Game
	var position *Position

	// "uci" command handler.
	doUci := func(args []string) {
		fmt.Println(`Donna v1.0.0 Copyright (c) 2014 by Michael Dvorkin. All Rights Reserved.`)
		fmt.Println(`id author Michael Dvorkin`)
		fmt.Println(`uciok`)
	}

	// "ucinewgame" command handler.
	doUciNewGame := func(args []string) {
		game, position = nil, nil
	}

	// "isready" command handler.
	doIsReady := func(args []string) {
		fmt.Println(`readyok`)
	}

	// "position [startpos | fen ] [ moves ... ]" command handler.
	doPosition := func(args []string) {
		fmt.Printf("%q\n", args)

		// Make sure we've started the game since "ucinewgame" is optional.
		if game == nil || position == nil {
			game = NewGame()
			position = game.Start()
		}

		switch args[0] {
		case `startpos`:
			args = args[1:]
			position = NewInitialPosition(game)
		case `fen`:
			fen := []string{}
			for _, token := range args[1:] {
				args = args[1:] // Shift the token.
				if token == `moves` {
					break
				}
				fen = append(fen, token)
			}
			fmt.Printf("fen: %s\n", strings.Join(fen, ` `))
			position = NewPositionFromFEN(game, strings.Join(fen, ` `))
		default: return
		}

		fmt.Printf("args: %q\n%s\n", args, position)
		if position != nil && len(args) > 0 && args[0] == `moves` {
			for _, move := range args[1:] {
				args = args[1:] // Shift the move.
				position = position.MakeMove(NewMoveFromNotation(position, move))
			}
		}
		fmt.Printf("%s\n", position)
	}

	// "go [[wtime winc | btime binc ] movestogo] | depth | nodes | movetime"
	doGo := func(args []string) {
		options := e.options
		fmt.Printf("%q\n", args)
		fmt.Printf("-> e.options: %+v\n", e.options)

		for i, token := range args {
			fmt.Printf("\t%d len %d Token [%v]\n", i, len(args), token)
			// Boolen "infinite" and "ponder" commands have no arguments.
			if token == `infinite` {
				options = Options{ infinite: true }
			} else if token == `ponder` {
				options = Options{ ponder: true }
			} else if len(args) > i+1 {
				switch token {
				case `depth`:
					if n, err := strconv.Atoi(args[i+1]); err == nil {
						options = Options{ maxDepth: n }
					}
				case `movetime`:
					if n, err := strconv.Atoi(args[i+1]); err == nil {
						options = Options{ maxDepth: n }
					}
				case `nodes`:
					if n, err := strconv.Atoi(args[i+1]); err == nil {
						options = Options{ maxNodes: n }
					}
				case `wtime`:
					if position.color == White {
						if n, err := strconv.Atoi(args[i+1]); err == nil {
							options.timeLeft = int64(n)
						}
					}
				case `btime`:
					if position.color == Black {
						if n, err := strconv.Atoi(args[i+1]); err == nil {
							options.timeLeft = int64(n)
						}
					}
				case `winc`:
					if position.color == White {
						if n, err := strconv.Atoi(args[i+1]); err == nil {
							options.timeInc = int64(n)
						}
					}
				case `binc`:
					if position.color == Black {
						if n, err := strconv.Atoi(args[i+1]); err == nil {
							options.timeInc = int64(n)
						}
					}
				case `movestogo`:
					if n, err := strconv.Atoi(args[i+1]); err == nil {
						options.movesToGo = n
					}
				}
			}
		}
		if options.timeLeft != 0 || options.timeInc != 0 || options.movesToGo != 0 {
			options.ponder, options.infinite = false, false
			options.maxDepth, options.maxNodes, options.moveTime = 0, 0, 0
		}
		e.options = options
		fmt.Printf("=> e.options: %+v\n", e.options)
	}

	// Stop calculating as soon as possible.
	doStop := func(args []string) {
		e.clock.halt = true
	}

	var commands = map[string]func([]string){
		`isready`: doIsReady,
		`uci`: doUci,
		`ucinewgame`: doUciNewGame,
		`position`: doPosition,
		`go`: doGo,
		`stop`: doStop,
	}

 	bio := bufio.NewReader(os.Stdin)
	for {
		command, err := bio.ReadString('\n')
		if err != io.EOF && len(command) > 0 {
			args := strings.Split(command[:len(command)-1], ` `)
			if args[0] == `quit` {
				break
			}
			if handler, ok := commands[args[0]]; ok {
				handler(args[1:])
			}
		}
	}
	return e
}
<|endoftext|>


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

// This file contains the code dealing with package directory trees.

package main

import (
	"bytes"
	"go/doc"
	"go/parser"
	"io/ioutil"
	"os"
	pathutil "path"
	"strings"
	"unicode"
)

type Directory struct {
	Depth int
	Path  string // includes Name
	Name  string
	Text  string       // package documentation, if any
	Dirs  []*Directory // subdirectories
}

func isGoFile(f *os.FileInfo) bool {
	return f.IsRegular() &&
		!strings.HasPrefix(f.Name, ".") && // ignore .files
		pathutil.Ext(f.Name) == ".go"
}

func isPkgFile(f *os.FileInfo) bool {
	return isGoFile(f) &&
		!strings.HasSuffix(f.Name, "_test.go") // ignore test files
}

func isPkgDir(f *os.FileInfo) bool {
	return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_'
}

func firstSentence(s string) string {
	i := -1 // index+1 of first terminator (punctuation ending a sentence)
	j := -1 // index+1 of first terminator followed by white space
	prev := 'A'
	for k, ch := range s {
		k1 := k + 1
		if ch == '.' || ch == '!' || ch == '?' {
			if i < 0 {
				i = k1 // first terminator
			}
			if k1 < len(s) && s[k1] <= ' ' {
				if j < 0 {
					j = k1 // first terminator followed by white space
				}
				if !unicode.IsUpper(prev) {
					j = k1
					break
				}
			}
		}
		prev = ch
	}

	if j < 0 {
		// use the next best terminator
		j = i
		if j < 0 {
			// no terminator at all, use the entire string
			j = len(s)
		}
	}

	return s[0:j]
}

type treeBuilder struct {
	pathFilter func(string) bool
	maxDepth   int
}

func (b *treeBuilder) newDirTree(path, name string, depth int) *Directory {
	if b.pathFilter != nil && !b.pathFilter(path) {
		return nil
	}

	if depth >= b.maxDepth {
		// return a dummy directory so that the parent directory
		// doesn't get discarded just because we reached the max
		// directory depth
		return &Directory{depth, path, name, "", nil}
	}

	list, _ := ioutil.ReadDir(path) // ignore errors

	// determine number of subdirectories and package files
	ndirs := 0
	nfiles := 0
	var synopses [4]string // prioritized package documentation (0 == highest priority)
	for _, d := range list {
		switch {
		case isPkgDir(d):
			ndirs++
		case isPkgFile(d):
			nfiles++
			if synopses[0] == "" {
				// no "optimal" package synopsis yet; continue to collect synopses
				file, err := parser.ParseFile(pathutil.Join(path, d.Name), nil,
					parser.ParseComments|parser.PackageClauseOnly)
				if err == nil && file.Doc != nil {
					// prioritize documentation
					i := -1
					switch file.Name.Name {
					case name:
						i = 0 // normal case: directory name matches package name
					case fakePkgName:
						i = 1 // synopses for commands
					case "main":
						i = 2 // directory contains a main package
					default:
						i = 3 // none of the above
					}
					if 0 <= i && i < len(synopses) && synopses[i] == "" {
						synopses[i] = firstSentence(doc.CommentText(file.Doc))
					}
				}
			}
		}
	}

	// create subdirectory tree
	var dirs []*Directory
	if ndirs > 0 {
		dirs = make([]*Directory, ndirs)
		i := 0
		for _, d := range list {
			if isPkgDir(d) {
				dd := b.newDirTree(pathutil.Join(path, d.Name), d.Name, depth+1)
				if dd != nil {
					dirs[i] = dd
					i++
				}
			}
		}
		dirs = dirs[0:i]
	}

	// if there are no package files and no subdirectories
	// (with package files), ignore the directory
	if nfiles == 0 && len(dirs) == 0 {
		return nil
	}

	// select the highest-priority synopsis for the directory entry, if any
	synopsis := ""
	for _, synopsis = range synopses {
		if synopsis != "" {
			break
		}
	}

	return &Directory{depth, path, name, synopsis, dirs}
}

// newDirectory creates a new package directory tree with at most maxDepth
// levels, anchored at root. The result tree is pruned such that it only
// contains directories that contain package files or that contain
// subdirectories containing package files (transitively). If a non-nil
// pathFilter is provided, directory paths additionally must be accepted
// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
// provided for maxDepth, nodes at larger depths are pruned as well; they
// are assumed to contain package files even if their contents are not known
// (i.e., in this case the tree may contain directories w/o any package files).
//
func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory {
	d, err := os.Lstat(root)
	if err != nil || !isPkgDir(d) {
		return nil
	}
	if maxDepth < 0 {
		maxDepth = 1e6 // "infinity"
	}
	b := treeBuilder{pathFilter, maxDepth}
	return b.newDirTree(root, d.Name, 0)
}

func (dir *Directory) writeLeafs(buf *bytes.Buffer) {
	if dir != nil {
		if len(dir.Dirs) == 0 {
			buf.WriteString(dir.Path)
			buf.WriteByte('\n')
			return
		}

		for _, d := range dir.Dirs {
			d.writeLeafs(buf)
		}
	}
}

func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
	if dir != nil {
		if !skipRoot {
			c <- dir
		}
		for _, d := range dir.Dirs {
			d.walk(c, false)
		}
	}
}

func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
	c := make(chan *Directory)
	go func() {
		dir.walk(c, skipRoot)
		close(c)
	}()
	return c
}

func (dir *Directory) lookupLocal(name string) *Directory {
	for _, d := range dir.Dirs {
		if d.Name == name {
			return d
		}
	}
	return nil
}

// lookup looks for the *Directory for a given path, relative to dir.
func (dir *Directory) lookup(path string) *Directory {
	d := strings.Split(dir.Path, "/", -1)
	p := strings.Split(path, "/", -1)
	i := 0
	for i < len(d) {
		if i >= len(p) || d[i] != p[i] {
			return nil
		}
		i++
	}
	for dir != nil && i < len(p) {
		dir = dir.lookupLocal(p[i])
		i++
	}
	return dir
}

// DirEntry describes a directory entry. The Depth and Height values
// are useful for presenting an entry in an indented fashion.
//
type DirEntry struct {
	Depth    int    // >= 0
	Height   int    // = DirList.MaxHeight - Depth, > 0
	Path     string // includes Name, relative to DirList root
	Name     string
	Synopsis string
}

type DirList struct {
	MaxHeight int // directory tree height, > 0
	List      []DirEntry
}

// listing creates a (linear) directory listing from a directory tree.
// If skipRoot is set, the root directory itself is excluded from the list.
//
func (root *Directory) listing(skipRoot bool) *DirList {
	if root == nil {
		return nil
	}

	// determine number of entries n and maximum height
	n := 0
	minDepth := 1 << 30 // infinity
	maxDepth := 0
	for d := range root.iter(skipRoot) {
		n++
		if minDepth > d.Depth {
			minDepth = d.Depth
		}
		if maxDepth < d.Depth {
			maxDepth = d.Depth
		}
	}
	maxHeight := maxDepth - minDepth + 1

	if n == 0 {
		return nil
	}

	// create list
	list := make([]DirEntry, n)
	i := 0
	for d := range root.iter(skipRoot) {
		p := &list[i]
		p.Depth = d.Depth - minDepth
		p.Height = maxHeight - p.Depth
		// the path is relative to root.Path - remove the root.Path
		// prefix (the prefix should always be present but avoid
		// crashes and check)
		path := d.Path
		if strings.HasPrefix(d.Path, root.Path) {
			path = d.Path[len(root.Path):]
		}
		// remove trailing '/' if any - path must be relative
		if len(path) > 0 && path[0] == '/' {
			path = path[1:]
		}
		p.Path = path
		p.Name = d.Name
		p.Synopsis = d.Text
		i++
	}

	return &DirList{maxHeight, list}
}
<commit_msg>godoc: only show directories containing true package files<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains the code dealing with package directory trees.

package main

import (
	"bytes"
	"go/doc"
	"go/parser"
	"io/ioutil"
	"os"
	pathutil "path"
	"strings"
	"unicode"
)

type Directory struct {
	Depth int
	Path  string // includes Name
	Name  string
	Text  string       // package documentation, if any
	Dirs  []*Directory // subdirectories
}

func isGoFile(f *os.FileInfo) bool {
	return f.IsRegular() &&
		!strings.HasPrefix(f.Name, ".") && // ignore .files
		pathutil.Ext(f.Name) == ".go"
}

func isPkgFile(f *os.FileInfo) bool {
	return isGoFile(f) &&
		!strings.HasSuffix(f.Name, "_test.go") // ignore test files
}

func isPkgDir(f *os.FileInfo) bool {
	return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_'
}

func firstSentence(s string) string {
	i := -1 // index+1 of first terminator (punctuation ending a sentence)
	j := -1 // index+1 of first terminator followed by white space
	prev := 'A'
	for k, ch := range s {
		k1 := k + 1
		if ch == '.' || ch == '!' || ch == '?' {
			if i < 0 {
				i = k1 // first terminator
			}
			if k1 < len(s) && s[k1] <= ' ' {
				if j < 0 {
					j = k1 // first terminator followed by white space
				}
				if !unicode.IsUpper(prev) {
					j = k1
					break
				}
			}
		}
		prev = ch
	}

	if j < 0 {
		// use the next best terminator
		j = i
		if j < 0 {
			// no terminator at all, use the entire string
			j = len(s)
		}
	}

	return s[0:j]
}

type treeBuilder struct {
	pathFilter func(string) bool
	maxDepth   int
}

func (b *treeBuilder) newDirTree(path, name string, depth int) *Directory {
	if b.pathFilter != nil && !b.pathFilter(path) {
		return nil
	}

	if depth >= b.maxDepth {
		// return a dummy directory so that the parent directory
		// doesn't get discarded just because we reached the max
		// directory depth
		return &Directory{depth, path, name, "", nil}
	}

	list, _ := ioutil.ReadDir(path) // ignore errors

	// determine number of subdirectories and if there are package files
	ndirs := 0
	hasPkgFiles := false
	var synopses [4]string // prioritized package documentation (0 == highest priority)
	for _, d := range list {
		switch {
		case isPkgDir(d):
			ndirs++
		case isPkgFile(d):
			// looks like a package file, but may just be a file ending in ".go";
			// don't just count it yet (otherwise we may end up with hasPkgFiles even
			// though the directory doesn't contain any real package files - was bug)
			if synopses[0] == "" {
				// no "optimal" package synopsis yet; continue to collect synopses
				file, err := parser.ParseFile(pathutil.Join(path, d.Name), nil,
					parser.ParseComments|parser.PackageClauseOnly)
				if err == nil {
					hasPkgFiles = true
					if file.Doc != nil {
						// prioritize documentation
						i := -1
						switch file.Name.Name {
						case name:
							i = 0 // normal case: directory name matches package name
						case fakePkgName:
							i = 1 // synopses for commands
						case "main":
							i = 2 // directory contains a main package
						default:
							i = 3 // none of the above
						}
						if 0 <= i && i < len(synopses) && synopses[i] == "" {
							synopses[i] = firstSentence(doc.CommentText(file.Doc))
						}
					}
				}
			}
		}
	}

	// create subdirectory tree
	var dirs []*Directory
	if ndirs > 0 {
		dirs = make([]*Directory, ndirs)
		i := 0
		for _, d := range list {
			if isPkgDir(d) {
				dd := b.newDirTree(pathutil.Join(path, d.Name), d.Name, depth+1)
				if dd != nil {
					dirs[i] = dd
					i++
				}
			}
		}
		dirs = dirs[0:i]
	}

	// if there are no package files and no subdirectories
	// containing package files, ignore the directory
	if !hasPkgFiles && len(dirs) == 0 {
		return nil
	}

	// select the highest-priority synopsis for the directory entry, if any
	synopsis := ""
	for _, synopsis = range synopses {
		if synopsis != "" {
			break
		}
	}

	return &Directory{depth, path, name, synopsis, dirs}
}

// newDirectory creates a new package directory tree with at most maxDepth
// levels, anchored at root. The result tree is pruned such that it only
// contains directories that contain package files or that contain
// subdirectories containing package files (transitively). If a non-nil
// pathFilter is provided, directory paths additionally must be accepted
// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
// provided for maxDepth, nodes at larger depths are pruned as well; they
// are assumed to contain package files even if their contents are not known
// (i.e., in this case the tree may contain directories w/o any package files).
//
func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory {
	d, err := os.Lstat(root)
	if err != nil || !isPkgDir(d) {
		return nil
	}
	if maxDepth < 0 {
		maxDepth = 1e6 // "infinity"
	}
	b := treeBuilder{pathFilter, maxDepth}
	return b.newDirTree(root, d.Name, 0)
}

func (dir *Directory) writeLeafs(buf *bytes.Buffer) {
	if dir != nil {
		if len(dir.Dirs) == 0 {
			buf.WriteString(dir.Path)
			buf.WriteByte('\n')
			return
		}

		for _, d := range dir.Dirs {
			d.writeLeafs(buf)
		}
	}
}

func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
	if dir != nil {
		if !skipRoot {
			c <- dir
		}
		for _, d := range dir.Dirs {
			d.walk(c, false)
		}
	}
}

func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
	c := make(chan *Directory)
	go func() {
		dir.walk(c, skipRoot)
		close(c)
	}()
	return c
}

func (dir *Directory) lookupLocal(name string) *Directory {
	for _, d := range dir.Dirs {
		if d.Name == name {
			return d
		}
	}
	return nil
}

// lookup looks for the *Directory for a given path, relative to dir.
func (dir *Directory) lookup(path string) *Directory {
	d := strings.Split(dir.Path, "/", -1)
	p := strings.Split(path, "/", -1)
	i := 0
	for i < len(d) {
		if i >= len(p) || d[i] != p[i] {
			return nil
		}
		i++
	}
	for dir != nil && i < len(p) {
		dir = dir.lookupLocal(p[i])
		i++
	}
	return dir
}

// DirEntry describes a directory entry. The Depth and Height values
// are useful for presenting an entry in an indented fashion.
//
type DirEntry struct {
	Depth    int    // >= 0
	Height   int    // = DirList.MaxHeight - Depth, > 0
	Path     string // includes Name, relative to DirList root
	Name     string
	Synopsis string
}

type DirList struct {
	MaxHeight int // directory tree height, > 0
	List      []DirEntry
}

// listing creates a (linear) directory listing from a directory tree.
// If skipRoot is set, the root directory itself is excluded from the list.
//
func (root *Directory) listing(skipRoot bool) *DirList {
	if root == nil {
		return nil
	}

	// determine number of entries n and maximum height
	n := 0
	minDepth := 1 << 30 // infinity
	maxDepth := 0
	for d := range root.iter(skipRoot) {
		n++
		if minDepth > d.Depth {
			minDepth = d.Depth
		}
		if maxDepth < d.Depth {
			maxDepth = d.Depth
		}
	}
	maxHeight := maxDepth - minDepth + 1

	if n == 0 {
		return nil
	}

	// create list
	list := make([]DirEntry, n)
	i := 0
	for d := range root.iter(skipRoot) {
		p := &list[i]
		p.Depth = d.Depth - minDepth
		p.Height = maxHeight - p.Depth
		// the path is relative to root.Path - remove the root.Path
		// prefix (the prefix should always be present but avoid
		// crashes and check)
		path := d.Path
		if strings.HasPrefix(d.Path, root.Path) {
			path = d.Path[len(root.Path):]
		}
		// remove trailing '/' if any - path must be relative
		if len(path) > 0 && path[0] == '/' {
			path = path[1:]
		}
		p.Path = path
		p.Name = d.Name
		p.Synopsis = d.Text
		i++
	}

	return &DirList{maxHeight, list}
}
<|endoftext|>


<commit_before>package sej

import "testing"

func TestReadThroughSegmentBoundary(t *testing.T) {
	messages := []string{"a", "b", "c"}

	path := newTestPath(t)
	w := newTestWriter(t, path, metaSize+1)
	writeTestMessages(t, w, messages...)
	closeTestWriter(t, w)

	r, err := NewScanner(path, 0)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Close()
	verifyReadMessages(t, path, messages...)
}

func TestReadFromOffset(t *testing.T) {
	messages := []string{"a", "b", "c", "d", "e"}
	for _, segmentSize := range []int{metaSize + 1, (metaSize + 1) * 2, 1000} {
		func() {
			path := newTestPath(t)
			w := newTestWriter(t, path, segmentSize)
			writeTestMessages(t, w, messages...)
			closeTestWriter(t, w)

			for i, expectedMsg := range messages {
				func() {
					// create a new reader starting from i
					r, err := NewScanner(path, uint64(i))
					if err != nil {
						t.Fatal(err)
					}
					defer r.Close()
					r.Scan()
					if r.Err() != nil {
						t.Fatal(r.Err())
					}
					actualMsg := string(r.Message().Value)
					if actualMsg != expectedMsg {
						t.Fatalf("expect msg %s, got %s", expectedMsg, actualMsg)
					}
					nextOffset := uint64(i + 1)
					if r.Offset() != nextOffset {
						t.Fatalf("expect offset %d, got %d", nextOffset, r.Offset())
					}
				}()
			}
		}()
	}
}

func TestReadBeforeWrite(t *testing.T) {
	messages := []string{"a", "b", "c", "d", "e"}
	for _, segmentSize := range []int{metaSize + 1, (metaSize + 1) * 2, 1000} {
		func() {
			path := newTestPath(t)
			r, err := NewScanner(path, 0)
			if err != nil {
				t.Fatal(err)
			}
			defer r.Close()

			done := make(chan bool)
			defer func() { <-done }()
			go func() {
				w := newTestWriter(t, path, segmentSize)
				writeTestMessages(t, w, messages...)
				closeTestWriter(t, w)
				done <- true
			}()
			for i := range messages {
				r.Scan()
				if r.Err() != nil {
					t.Fatal(r.Err())
				}
				actualMsg, expectedMsg := string(r.Message().Value), messages[i]
				if actualMsg != expectedMsg {
					t.Fatalf("expect msg %s, got %s", expectedMsg, actualMsg)
				}
			}
		}()
	}
}
<commit_msg>test reading monitored file<commit_after>package sej

import (
	"testing"
	"time"
)

func TestReadThroughSegmentBoundary(t *testing.T) {
	messages := []string{"a", "b", "c"}

	path := newTestPath(t)
	w := newTestWriter(t, path, metaSize+1)
	writeTestMessages(t, w, messages...)
	closeTestWriter(t, w)

	r, err := NewScanner(path, 0)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Close()
	verifyReadMessages(t, path, messages...)
}

func TestReadFromOffset(t *testing.T) {
	messages := []string{"a", "b", "c", "d", "e"}
	for _, segmentSize := range []int{metaSize + 1, (metaSize + 1) * 2, 1000} {
		func() {
			path := newTestPath(t)
			w := newTestWriter(t, path, segmentSize)
			writeTestMessages(t, w, messages...)
			closeTestWriter(t, w)

			for i, expectedMsg := range messages {
				func() {
					// create a new reader starting from i
					r, err := NewScanner(path, uint64(i))
					if err != nil {
						t.Fatal(err)
					}
					defer r.Close()
					r.Scan()
					if r.Err() != nil {
						t.Fatal(r.Err())
					}
					actualMsg := string(r.Message().Value)
					if actualMsg != expectedMsg {
						t.Fatalf("expect msg %s, got %s", expectedMsg, actualMsg)
					}
					nextOffset := uint64(i + 1)
					if r.Offset() != nextOffset {
						t.Fatalf("expect offset %d, got %d", nextOffset, r.Offset())
					}
				}()
			}
		}()
	}
}

func TestReadBeforeWrite(t *testing.T) {
	messages := []string{"a", "b", "c", "d", "e"}
	for _, segmentSize := range []int{metaSize + 1, (metaSize + 1) * 2, 1000} {
		func() {
			path := newTestPath(t)
			r, err := NewScanner(path, 0)
			if err != nil {
				t.Fatal(err)
			}
			defer r.Close()

			done := make(chan bool)
			defer func() { <-done }()
			go func() {
				w := newTestWriter(t, path, segmentSize)
				writeTestMessages(t, w, messages...)
				closeTestWriter(t, w)
				done <- true
			}()
			for i := range messages {
				if !r.Scan() {
					t.Fatal("Scan should return true")
				}
				if r.Err() != nil {
					t.Fatal(r.Err())
				}
				actualMsg, expectedMsg := string(r.Message().Value), messages[i]
				if actualMsg != expectedMsg {
					t.Fatalf("expect msg %s, got %s", expectedMsg, actualMsg)
				}
			}
		}()
	}
}

func TestReadMonitoredFile(t *testing.T) {
	path := newTestPath(t)
	w := newTestWriter(t, path, 1000)

	r, err := NewScanner(path, 0)
	r.Timeout = time.Second
	if err != nil {
		t.Fatal(err)
	}
	defer r.Close()

	writeTestMessages(t, w, "a")
	flushTestWriter(t, w)

	if !r.Scan() {
		t.Fatal("Scan should return true")
	}
	if r.Err() != nil {
		t.Fatal(r.Err())
	}
	actualMsg, expectedMsg := string(r.Message().Value), "a"
	if actualMsg != expectedMsg {
		t.Fatalf("expect msg %s, got %s", expectedMsg, actualMsg)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 tsuru-client 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 installer

import (
	"encoding/json"
	"net/http"
	"path/filepath"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/fsouza/go-dockerclient/testing"
	"gopkg.in/check.v1"
)

type testEndpoint struct {
	endpoint string
	certPath string
}

func (t testEndpoint) dockerClient() (*docker.Client, error) {
	return docker.NewTLSClient(
		t.endpoint,
		filepath.Join(t.certPath, "cert.pem"),
		filepath.Join(t.certPath, "key.pem"),
		filepath.Join(t.certPath, "ca.pem"),
	)
}

func (s *S) TestCreateContainer(c *check.C) {
	var requests []*http.Request
	tlsConfig := testing.TLSConfig{
		CertPath:    s.TLSCertsPath.ServerCert,
		CertKeyPath: s.TLSCertsPath.ServerKey,
		RootCAPath:  s.TLSCertsPath.RootCert,
	}
	server, err := testing.NewTLSServer("127.0.0.1:0", nil, func(r *http.Request) {
		if r.URL.Path != "/version" {
			requests = append(requests, r)
		}
	}, tlsConfig)
	c.Assert(err, check.IsNil)
	defer server.Stop()
	endpoint := testEndpoint{endpoint: server.URL(), certPath: s.TLSCertsPath.RootDir}
	config := &docker.Config{Image: "tsuru/api:v1"}
	err = createContainer(endpoint, "contName", config, nil)
	c.Assert(err, check.IsNil)
	c.Assert(requests, check.HasLen, 4)
	c.Assert(requests[0].URL.Path, check.Equals, "/images/create")
	c.Assert(requests[1].URL.Path, check.Equals, "/images/tsuru/api:v1/json")
	c.Assert(requests[2].URL.Path, check.Equals, "/containers/create")
	c.Assert(requests[3].URL.Path, check.Equals, "/containers/contName/start")
}

func (s *S) TestCreateContainerWithExposedPorts(c *check.C) {
	containerChan := make(chan *docker.Container, 2)
	tlsConfig := testing.TLSConfig{
		CertPath:    s.TLSCertsPath.ServerCert,
		CertKeyPath: s.TLSCertsPath.ServerKey,
		RootCAPath:  s.TLSCertsPath.RootCert,
	}
	server, err := testing.NewTLSServer("127.0.0.1:0", containerChan, nil, tlsConfig)
	c.Assert(err, check.IsNil)
	defer server.Stop()
	server.CustomHandler("/images/.*/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		image := docker.Image{
			ID: "tsuru/api",
			Config: &docker.Config{
				ExposedPorts: map[docker.Port]struct{}{
					docker.Port("90/tcp"): {},
				},
			},
		}
		buf, errMarshal := json.Marshal(image)
		c.Assert(errMarshal, check.IsNil)
		w.Header().Set("Content-Type", "application/json")
		w.Write(buf)
		w.WriteHeader(http.StatusOK)
	}))
	defer server.CustomHandler("/images/.*/json", server.DefaultHandler())
	expected := map[docker.Port][]docker.PortBinding{
		"90/tcp": []docker.PortBinding{
			docker.PortBinding{HostIP: "0.0.0.0", HostPort: "90"},
		},
	}
	endpoint := testEndpoint{endpoint: server.URL(), certPath: s.TLSCertsPath.RootDir}
	config := &docker.Config{Image: "tsuru/api:v1"}
	err = createContainer(endpoint, "contName", config, nil)
	c.Assert(err, check.IsNil)
	cont := <-containerChan
	c.Assert(cont, check.NotNil)
	c.Assert(expected, check.DeepEquals, cont.HostConfig.PortBindings)
}

func (s *S) TestCreateContainerWithHostConfigAndExposedPorts(c *check.C) {
	containerChan := make(chan *docker.Container, 2)
	tlsConfig := testing.TLSConfig{
		CertPath:    s.TLSCertsPath.ServerCert,
		CertKeyPath: s.TLSCertsPath.ServerKey,
		RootCAPath:  s.TLSCertsPath.RootCert,
	}
	server, err := testing.NewTLSServer("127.0.0.1:0", containerChan, nil, tlsConfig)
	c.Assert(err, check.IsNil)
	defer server.Stop()
	server.CustomHandler("/images/.*/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		image := docker.Image{
			ID: "tsuru/api",
			Config: &docker.Config{
				ExposedPorts: map[docker.Port]struct{}{
					docker.Port("90/tcp"): {},
				},
			},
		}
		buf, errMarshal := json.Marshal(image)
		c.Assert(errMarshal, check.IsNil)
		w.Header().Set("Content-Type", "application/json")
		w.Write(buf)
		w.WriteHeader(http.StatusOK)
	}))
	defer server.CustomHandler("/images/.*/json", server.DefaultHandler())
	expected := map[docker.Port][]docker.PortBinding{
		"90/tcp": []docker.PortBinding{
			docker.PortBinding{HostIP: "0.0.0.0", HostPort: "90"},
		},
		"100/tcp": []docker.PortBinding{
			docker.PortBinding{HostIP: "0.0.0.0", HostPort: "100"},
		},
	}
	endpoint := testEndpoint{endpoint: server.URL(), certPath: s.TLSCertsPath.RootDir}
	config := &docker.Config{Image: "tsuru/api:v1"}
	hostConfig := &docker.HostConfig{
		PortBindings: map[docker.Port][]docker.PortBinding{
			"100/tcp": []docker.PortBinding{
				docker.PortBinding{HostIP: "0.0.0.0", HostPort: "100"},
			},
		},
	}
	err = createContainer(endpoint, "contName", config, hostConfig)
	c.Assert(err, check.IsNil)
	cont := <-containerChan
	c.Assert(cont, check.NotNil)
	c.Assert(expected, check.DeepEquals, cont.HostConfig.PortBindings)
}
<commit_msg>installer: simplify code<commit_after>// Copyright 2016 tsuru-client 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 installer

import (
	"encoding/json"
	"net/http"
	"path/filepath"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/fsouza/go-dockerclient/testing"
	"gopkg.in/check.v1"
)

type testEndpoint struct {
	endpoint string
	certPath string
}

func (t testEndpoint) dockerClient() (*docker.Client, error) {
	return docker.NewTLSClient(
		t.endpoint,
		filepath.Join(t.certPath, "cert.pem"),
		filepath.Join(t.certPath, "key.pem"),
		filepath.Join(t.certPath, "ca.pem"),
	)
}

func (s *S) TestCreateContainer(c *check.C) {
	var requests []*http.Request
	tlsConfig := testing.TLSConfig{
		CertPath:    s.TLSCertsPath.ServerCert,
		CertKeyPath: s.TLSCertsPath.ServerKey,
		RootCAPath:  s.TLSCertsPath.RootCert,
	}
	server, err := testing.NewTLSServer("127.0.0.1:0", nil, func(r *http.Request) {
		if r.URL.Path != "/version" {
			requests = append(requests, r)
		}
	}, tlsConfig)
	c.Assert(err, check.IsNil)
	defer server.Stop()
	endpoint := testEndpoint{endpoint: server.URL(), certPath: s.TLSCertsPath.RootDir}
	config := &docker.Config{Image: "tsuru/api:v1"}
	err = createContainer(endpoint, "contName", config, nil)
	c.Assert(err, check.IsNil)
	c.Assert(requests, check.HasLen, 4)
	c.Assert(requests[0].URL.Path, check.Equals, "/images/create")
	c.Assert(requests[1].URL.Path, check.Equals, "/images/tsuru/api:v1/json")
	c.Assert(requests[2].URL.Path, check.Equals, "/containers/create")
	c.Assert(requests[3].URL.Path, check.Equals, "/containers/contName/start")
}

func (s *S) TestCreateContainerWithExposedPorts(c *check.C) {
	containerChan := make(chan *docker.Container, 2)
	tlsConfig := testing.TLSConfig{
		CertPath:    s.TLSCertsPath.ServerCert,
		CertKeyPath: s.TLSCertsPath.ServerKey,
		RootCAPath:  s.TLSCertsPath.RootCert,
	}
	server, err := testing.NewTLSServer("127.0.0.1:0", containerChan, nil, tlsConfig)
	c.Assert(err, check.IsNil)
	defer server.Stop()
	server.CustomHandler("/images/.*/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		image := docker.Image{
			ID: "tsuru/api",
			Config: &docker.Config{
				ExposedPorts: map[docker.Port]struct{}{
					docker.Port("90/tcp"): {},
				},
			},
		}
		buf, errMarshal := json.Marshal(image)
		c.Assert(errMarshal, check.IsNil)
		w.Header().Set("Content-Type", "application/json")
		w.Write(buf)
		w.WriteHeader(http.StatusOK)
	}))
	defer server.CustomHandler("/images/.*/json", server.DefaultHandler())
	expected := map[docker.Port][]docker.PortBinding{
		"90/tcp": {
			{HostIP: "0.0.0.0", HostPort: "90"},
		},
	}
	endpoint := testEndpoint{endpoint: server.URL(), certPath: s.TLSCertsPath.RootDir}
	config := &docker.Config{Image: "tsuru/api:v1"}
	err = createContainer(endpoint, "contName", config, nil)
	c.Assert(err, check.IsNil)
	cont := <-containerChan
	c.Assert(cont, check.NotNil)
	c.Assert(expected, check.DeepEquals, cont.HostConfig.PortBindings)
}

func (s *S) TestCreateContainerWithHostConfigAndExposedPorts(c *check.C) {
	containerChan := make(chan *docker.Container, 2)
	tlsConfig := testing.TLSConfig{
		CertPath:    s.TLSCertsPath.ServerCert,
		CertKeyPath: s.TLSCertsPath.ServerKey,
		RootCAPath:  s.TLSCertsPath.RootCert,
	}
	server, err := testing.NewTLSServer("127.0.0.1:0", containerChan, nil, tlsConfig)
	c.Assert(err, check.IsNil)
	defer server.Stop()
	server.CustomHandler("/images/.*/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		image := docker.Image{
			ID: "tsuru/api",
			Config: &docker.Config{
				ExposedPorts: map[docker.Port]struct{}{
					docker.Port("90/tcp"): {},
				},
			},
		}
		buf, errMarshal := json.Marshal(image)
		c.Assert(errMarshal, check.IsNil)
		w.Header().Set("Content-Type", "application/json")
		w.Write(buf)
		w.WriteHeader(http.StatusOK)
	}))
	defer server.CustomHandler("/images/.*/json", server.DefaultHandler())
	expected := map[docker.Port][]docker.PortBinding{
		"90/tcp": {
			{HostIP: "0.0.0.0", HostPort: "90"},
		},
		"100/tcp": {
			{HostIP: "0.0.0.0", HostPort: "100"},
		},
	}
	endpoint := testEndpoint{endpoint: server.URL(), certPath: s.TLSCertsPath.RootDir}
	config := &docker.Config{Image: "tsuru/api:v1"}
	hostConfig := &docker.HostConfig{
		PortBindings: map[docker.Port][]docker.PortBinding{
			"100/tcp": {
				{HostIP: "0.0.0.0", HostPort: "100"},
			},
		},
	}
	err = createContainer(endpoint, "contName", config, hostConfig)
	c.Assert(err, check.IsNil)
	cont := <-containerChan
	c.Assert(cont, check.NotNil)
	c.Assert(expected, check.DeepEquals, cont.HostConfig.PortBindings)
}
<|endoftext|>


<commit_before>package ledis

import (
	"fmt"
	"testing"
)

const (
	endPos int = -1
)

func bin(sz string) []byte {
	return []byte(sz)
}

func pair(memb string, score int) ScorePair {
	return ScorePair{int64(score), bin(memb)}
}

func TestZSetCodec(t *testing.T) {
	db := getTestDB()

	key := []byte("key")
	member := []byte("member")

	ek := db.zEncodeSizeKey(key)
	if k, err := db.zDecodeSizeKey(ek); err != nil {
		t.Fatal(err)
	} else if string(k) != "key" {
		t.Fatal(string(k))
	}

	ek = db.zEncodeSetKey(key, member)
	if k, m, err := db.zDecodeSetKey(ek); err != nil {
		t.Fatal(err)
	} else if string(k) != "key" {
		t.Fatal(string(k))
	} else if string(m) != "member" {
		t.Fatal(string(m))
	}

	ek = db.zEncodeScoreKey(key, member, 100)
	if k, m, s, err := db.zDecodeScoreKey(ek); err != nil {
		t.Fatal(err)
	} else if string(k) != "key" {
		t.Fatal(string(k))
	} else if string(m) != "member" {
		t.Fatal(string(m))
	} else if s != 100 {
		t.Fatal(s)
	}

}

func TestDBZSet(t *testing.T) {
	db := getTestDB()

	key := bin("testdb_zset_a")

	// {'a':0, 'b':1, 'c':2, 'd':3}
	if n, err := db.ZAdd(key, pair("a", 0), pair("b", 1),
		pair("c", 2), pair("d", 3)); err != nil {
		t.Fatal(err)
	} else if n != 4 {
		t.Fatal(n)
	}

	if n, err := db.ZCount(key, 0, 0XFF); err != nil {
		t.Fatal(err)
	} else if n != 4 {
		t.Fatal(n)
	}

	// {c':2, 'd':3}
	if n, err := db.ZRem(key, bin("a"), bin("b")); err != nil {
		t.Fatal(err)
	} else if n != 2 {
		t.Fatal(n)
	}

	if n, err := db.ZRem(key, bin("a"), bin("b")); err != nil {
		t.Fatal(err)
	} else if n != 0 {
		t.Fatal(n)
	}

	if n, err := db.ZCard(key); err != nil {
		t.Fatal(err)
	} else if n != 2 {
		t.Fatal(n)
	}

	// {}
	if n, err := db.ZClear(key); err != nil {
		t.Fatal(err)
	} else if n != 2 {
		t.Fatal(n)
	}

	if n, err := db.ZCount(key, 0, 0XFF); err != nil {
		t.Fatal(err)
	} else if n != 0 {
		t.Fatal(n)
	}
}

func TestZSetOrder(t *testing.T) {
	db := getTestDB()

	key := bin("testdb_zset_order")

	// {'a':0, 'b':1, 'c':2, 'd':3, 'e':4, 'f':5}
	membs := [...]string{"a", "b", "c", "d", "e", "f"}
	membCnt := len(membs)

	for i := 0; i < membCnt; i++ {
		db.ZAdd(key, pair(membs[i], i))
	}

	if n, _ := db.ZCount(key, 0, 0XFFFF); int(n) != membCnt {
		t.Fatal(n)
	}

	for i := 0; i < membCnt; i++ {
		if pos, err := db.ZRank(key, bin(membs[i])); err != nil {
			t.Fatal(err)
		} else if int(pos) != i {
			t.Fatal(pos)
		}

		if pos, err := db.ZRevRank(key, bin(membs[i])); err != nil {
			t.Fatal(err)
		} else if int(pos) != membCnt-i-1 {
			t.Fatal(pos)
		}
	}

	if qMembs, err := db.ZRange(key, 0, endPos, false); err != nil {
		t.Fatal(err)
	} else if len(qMembs) != membCnt {
		t.Fatal(fmt.Sprintf("%d vs %d", len(qMembs), membCnt))
	} else {
		for i := 0; i < membCnt; i++ {
			if memb, ok := qMembs[i].([]byte); !ok {
				t.Fatal(ok)
			} else if string(memb) != membs[i] {
				t.Fatal(fmt.Sprintf("[%s] vs [%s]", qMembs[i], membs[i]))
			}
		}
	}

	// {'a':0, 'b':1, 'c':2, 'd':999, 'e':4, 'f':5}
	if n, err := db.ZAdd(key, pair("d", 999)); err != nil {
		t.Fatal(err)
	} else if n != 0 {
		t.Fatal(n)
	}

	if pos, _ := db.ZRank(key, bin("d")); int(pos) != membCnt-1 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRevRank(key, bin("d")); int(pos) != 0 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRank(key, bin("e")); int(pos) != 3 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRank(key, bin("f")); int(pos) != 4 {
		t.Fatal(pos)
	}

	if qMembs, err := db.ZRangeByScore(key, 999, 0XFFFF, false, 0, membCnt); err != nil {
		t.Fatal(err)
	} else if len(qMembs) != 1 {
		t.Fatal(len(qMembs))
	}

	// {'a':0, 'b':1, 'c':2, 'd':999, 'e':6, 'f':5}
	if _, err := db.ZIncrBy(key, 2, bin("e")); err != nil {
		t.Fatal(err)
	}

	if pos, _ := db.ZRank(key, bin("e")); int(pos) != 4 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRevRank(key, bin("e")); int(pos) != 1 {
		t.Fatal(pos)
	}

	if dropCnt, _ := testLedis.FlushDB(); dropCnt <= 0 {
		t.Fatal(dropCnt)
	}

	if n, _ := db.ZCount(key, 0, 0XFFFF); n > 0 {
		t.Fatal(n)
	}
}
<commit_msg>remove 'FlushDB' from test<commit_after>package ledis

import (
	"fmt"
	"testing"
)

const (
	endPos int = -1
)

func bin(sz string) []byte {
	return []byte(sz)
}

func pair(memb string, score int) ScorePair {
	return ScorePair{int64(score), bin(memb)}
}

func TestZSetCodec(t *testing.T) {
	db := getTestDB()

	key := []byte("key")
	member := []byte("member")

	ek := db.zEncodeSizeKey(key)
	if k, err := db.zDecodeSizeKey(ek); err != nil {
		t.Fatal(err)
	} else if string(k) != "key" {
		t.Fatal(string(k))
	}

	ek = db.zEncodeSetKey(key, member)
	if k, m, err := db.zDecodeSetKey(ek); err != nil {
		t.Fatal(err)
	} else if string(k) != "key" {
		t.Fatal(string(k))
	} else if string(m) != "member" {
		t.Fatal(string(m))
	}

	ek = db.zEncodeScoreKey(key, member, 100)
	if k, m, s, err := db.zDecodeScoreKey(ek); err != nil {
		t.Fatal(err)
	} else if string(k) != "key" {
		t.Fatal(string(k))
	} else if string(m) != "member" {
		t.Fatal(string(m))
	} else if s != 100 {
		t.Fatal(s)
	}

}

func TestDBZSet(t *testing.T) {
	db := getTestDB()

	key := bin("testdb_zset_a")

	// {'a':0, 'b':1, 'c':2, 'd':3}
	if n, err := db.ZAdd(key, pair("a", 0), pair("b", 1),
		pair("c", 2), pair("d", 3)); err != nil {
		t.Fatal(err)
	} else if n != 4 {
		t.Fatal(n)
	}

	if n, err := db.ZCount(key, 0, 0XFF); err != nil {
		t.Fatal(err)
	} else if n != 4 {
		t.Fatal(n)
	}

	// {c':2, 'd':3}
	if n, err := db.ZRem(key, bin("a"), bin("b")); err != nil {
		t.Fatal(err)
	} else if n != 2 {
		t.Fatal(n)
	}

	if n, err := db.ZRem(key, bin("a"), bin("b")); err != nil {
		t.Fatal(err)
	} else if n != 0 {
		t.Fatal(n)
	}

	if n, err := db.ZCard(key); err != nil {
		t.Fatal(err)
	} else if n != 2 {
		t.Fatal(n)
	}

	// {}
	if n, err := db.ZClear(key); err != nil {
		t.Fatal(err)
	} else if n != 2 {
		t.Fatal(n)
	}

	if n, err := db.ZCount(key, 0, 0XFF); err != nil {
		t.Fatal(err)
	} else if n != 0 {
		t.Fatal(n)
	}
}

func TestZSetOrder(t *testing.T) {
	db := getTestDB()

	key := bin("testdb_zset_order")

	// {'a':0, 'b':1, 'c':2, 'd':3, 'e':4, 'f':5}
	membs := [...]string{"a", "b", "c", "d", "e", "f"}
	membCnt := len(membs)

	for i := 0; i < membCnt; i++ {
		db.ZAdd(key, pair(membs[i], i))
	}

	if n, _ := db.ZCount(key, 0, 0XFFFF); int(n) != membCnt {
		t.Fatal(n)
	}

	for i := 0; i < membCnt; i++ {
		if pos, err := db.ZRank(key, bin(membs[i])); err != nil {
			t.Fatal(err)
		} else if int(pos) != i {
			t.Fatal(pos)
		}

		if pos, err := db.ZRevRank(key, bin(membs[i])); err != nil {
			t.Fatal(err)
		} else if int(pos) != membCnt-i-1 {
			t.Fatal(pos)
		}
	}

	if qMembs, err := db.ZRange(key, 0, endPos, false); err != nil {
		t.Fatal(err)
	} else if len(qMembs) != membCnt {
		t.Fatal(fmt.Sprintf("%d vs %d", len(qMembs), membCnt))
	} else {
		for i := 0; i < membCnt; i++ {
			if memb, ok := qMembs[i].([]byte); !ok {
				t.Fatal(ok)
			} else if string(memb) != membs[i] {
				t.Fatal(fmt.Sprintf("[%s] vs [%s]", qMembs[i], membs[i]))
			}
		}
	}

	// {'a':0, 'b':1, 'c':2, 'd':999, 'e':4, 'f':5}
	if n, err := db.ZAdd(key, pair("d", 999)); err != nil {
		t.Fatal(err)
	} else if n != 0 {
		t.Fatal(n)
	}

	if pos, _ := db.ZRank(key, bin("d")); int(pos) != membCnt-1 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRevRank(key, bin("d")); int(pos) != 0 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRank(key, bin("e")); int(pos) != 3 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRank(key, bin("f")); int(pos) != 4 {
		t.Fatal(pos)
	}

	if qMembs, err := db.ZRangeByScore(key, 999, 0XFFFF, false, 0, membCnt); err != nil {
		t.Fatal(err)
	} else if len(qMembs) != 1 {
		t.Fatal(len(qMembs))
	}

	// {'a':0, 'b':1, 'c':2, 'd':999, 'e':6, 'f':5}
	if _, err := db.ZIncrBy(key, 2, bin("e")); err != nil {
		t.Fatal(err)
	}

	if pos, _ := db.ZRank(key, bin("e")); int(pos) != 4 {
		t.Fatal(pos)
	}

	if pos, _ := db.ZRevRank(key, bin("e")); int(pos) != 1 {
		t.Fatal(pos)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	"io"
	"log"
	"net/http"
	"os"
	"path"
)

type Event struct {
	ContainerId string `json:"id"`
	Status      string `json:"status"`
	Image       string `json:"from"`
}

type ContainerConfig struct {
	Hostname string
}

type NetworkSettings struct {
	IpAddress   string
	PortMapping map[string]map[string]string
}

type Container struct {
	Id              string
	Image           string
	Config          *ContainerConfig
	NetworkSettings *NetworkSettings
}

type Config struct {
	RedisProto string               `toml:"redisproto"`
	RedisAddr  string               `toml:"redisaddr"`
	DockerApi  string               `toml:"docker"`
	Routes     map[string]RouteHost `toml:"routes"`
}

type RouteHost struct {
	Hostnames []string `toml:"hostname"`
	Port      string   `toml:"port"`
}

type Handler func(c *Container) error

func inspectContainer(id string, image string, c http.Client, config Config) (*Container, error) {
	// Use the container id to fetch the container json from the Remote API
	// http://docs.docker.io/en/latest/api/docker_remote_api_v1.4/#inspect-a-container
	res, err := c.Get(fmt.Sprintf("%s/containers/%s/json", config.DockerApi))
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode == http.StatusOK {
		d := json.NewDecoder(res.Body)

		var container Container
		if err = d.Decode(&container); err != nil {
			return nil, err
		}
		container.Image = image
		return &container, nil
	}
	return nil, fmt.Errorf("Invalid status code from api: %d", res.StatusCode)
}

func initRoutes(config Config, h *Hipache) error {
	for _, route := range config.Routes {
		for _, hostname := range route.Hostnames {
			if err := h.CreateRoute(hostname); err != nil {
				return err
			}
		}
	}
	return nil
}

func setupHandlers(h *Hipache) map[string]Handler {
	out := make(map[string]Handler, 2)

	out["start"] = h.AddRoute

	out["stop"] = func(c *Container) error {
		hostPort, err := h.FetchRoutes(c)
		if err != nil {
			return err
		}

		for host, port := range hostPort {
			if err := h.RemoveRoute(host, port); err != nil {
				return err
			}
		}
		return nil
	}

	return out
}

func main() {
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	var config Config
	configPath := flag.String("conf", path.Join(cwd, "docker-hipache.toml"), "Path to the toml config file")
	if _, err := toml.DecodeFile(*configPath, &config); err != nil {
		log.Fatalf("Could not load config: %s", err)
		return
	}

	h := NewHipache(config.RedisProto, config.RedisAddr, config.Routes)

	if err := initRoutes(config, h); err != nil {
		log.Fatalf("Could not init routes: %s", err)
		return
	}

	handlers := setupHandlers(h)

	c := http.Client{}
	res, err := c.Get(fmt.Sprintf("%s/events", config.DockerApi))
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	// Read the streaming json from the events endpoint
	// http://docs.docker.io/en/latest/api/docker_remote_api_v1.3/#monitor-docker-s-events
	d := json.NewDecoder(res.Body)
	for {
		log.Println("New event received")

		var event Event
		if err := d.Decode(&event); err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}
		if handler, exists := handlers[event.Status]; exists {
			container, err := inspectContainer(event.ContainerId, event.Image, c, config)
			if err != nil {
				log.Fatalf("Could not inspect container: %s", err)
				continue
			}

			if err := handler(container); err != nil {
				log.Fatalf("Could not handle event. Id: %s, Image: %s, Error: %s", event.ContainerId, event.Image, err)
				continue
			}
		}
	}
	log.Println("Event stream complete, shutting down")
}
<commit_msg>Add missing flag.Parse<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	"io"
	"log"
	"net/http"
	"os"
	"path"
)

type Event struct {
	ContainerId string `json:"id"`
	Status      string `json:"status"`
	Image       string `json:"from"`
}

type ContainerConfig struct {
	Hostname string
}

type NetworkSettings struct {
	IpAddress   string
	PortMapping map[string]map[string]string
}

type Container struct {
	Id              string
	Image           string
	Config          *ContainerConfig
	NetworkSettings *NetworkSettings
}

type Config struct {
	RedisProto string               `toml:"redisproto"`
	RedisAddr  string               `toml:"redisaddr"`
	DockerApi  string               `toml:"docker"`
	Routes     map[string]RouteHost `toml:"routes"`
}

type RouteHost struct {
	Hostnames []string `toml:"hostname"`
	Port      string   `toml:"port"`
}

type Handler func(c *Container) error

func inspectContainer(id string, image string, c http.Client, config Config) (*Container, error) {
	// Use the container id to fetch the container json from the Remote API
	// http://docs.docker.io/en/latest/api/docker_remote_api_v1.4/#inspect-a-container
	res, err := c.Get(fmt.Sprintf("%s/containers/%s/json", config.DockerApi))
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode == http.StatusOK {
		d := json.NewDecoder(res.Body)

		var container Container
		if err = d.Decode(&container); err != nil {
			return nil, err
		}
		container.Image = image
		return &container, nil
	}
	return nil, fmt.Errorf("Invalid status code from api: %d", res.StatusCode)
}

func initRoutes(config Config, h *Hipache) error {
	for _, route := range config.Routes {
		for _, hostname := range route.Hostnames {
			if err := h.CreateRoute(hostname); err != nil {
				return err
			}
		}
	}
	return nil
}

func setupHandlers(h *Hipache) map[string]Handler {
	out := make(map[string]Handler, 2)

	out["start"] = h.AddRoute

	out["stop"] = func(c *Container) error {
		hostPort, err := h.FetchRoutes(c)
		if err != nil {
			return err
		}

		for host, port := range hostPort {
			if err := h.RemoveRoute(host, port); err != nil {
				return err
			}
		}
		return nil
	}

	return out
}

func main() {
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	var config Config
	configPath := flag.String("conf", path.Join(cwd, "docker-hipache.toml"), "Path to the toml config file")

	flag.Parse()

	if _, err := toml.DecodeFile(*configPath, &config); err != nil {
		log.Fatalf("Could not load config: %s", err)
		return
	}

	h := NewHipache(config.RedisProto, config.RedisAddr, config.Routes)

	if err := initRoutes(config, h); err != nil {
		log.Fatalf("Could not init routes: %s", err)
		return
	}

	handlers := setupHandlers(h)

	c := http.Client{}
	res, err := c.Get(fmt.Sprintf("%s/events", config.DockerApi))
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	// Read the streaming json from the events endpoint
	// http://docs.docker.io/en/latest/api/docker_remote_api_v1.3/#monitor-docker-s-events
	d := json.NewDecoder(res.Body)
	for {
		log.Println("New event received")

		var event Event
		if err := d.Decode(&event); err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}
		if handler, exists := handlers[event.Status]; exists {
			container, err := inspectContainer(event.ContainerId, event.Image, c, config)
			if err != nil {
				log.Fatalf("Could not inspect container: %s", err)
				continue
			}

			if err := handler(container); err != nil {
				log.Fatalf("Could not handle event. Id: %s, Image: %s, Error: %s", event.ContainerId, event.Image, err)
				continue
			}
		}
	}
	log.Println("Event stream complete, shutting down")
}
<|endoftext|>