<commit_before>package irmaclient

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"math"
	"time"

	"github.com/privacybydesign/gabi/revocation"
	irma "github.com/privacybydesign/irmago"
	"github.com/sirupsen/logrus"
)

func (client *Client) initRevocation() {
	// For every credential supporting revocation, compute nonrevocation caches in async jobs
	for typ, attrsets := range client.attributes {
		for i, attrs := range attrsets {
			if attrs.CredentialType() == nil || !attrs.CredentialType().RevocationSupported() {
				continue
			}
			typ := typ // make copy of same name to capture the value for closure below
			i := i     // see https://golang.org/doc/faq#closures_and_goroutines
			client.jobs <- func() {
				if err := client.nonrevPrepareCache(typ, i); err != nil {
					client.reportError(err)
				}
			}
		}
	}

	// Of each credential supporting revocation, we periodically update its nonrevocation witness
	// by fetching updates from the issuer's server, such that:
	// - The time interval between two updates is random so that the server cannot recognize us
	//   using the update interval,
	// - Updating happens regularly even if the app is rarely used.
	// We do this by every 10 seconds updating the credential with a low probability, which
	// increases over time since the last update.
	client.Configuration.Scheduler.Every(irma.RevocationParameters.ClientUpdateInterval).Seconds().Do(func() {
		for typ, attrsets := range client.attributes {
			for i, attrs := range attrsets {
				if attrs.CredentialType() == nil || !attrs.CredentialType().RevocationSupported() {
					continue
				}
				cred, err := client.credential(typ, i)
				if err != nil {
					client.reportError(err)
					continue
				}
				if cred.NonRevocationWitness == nil {
					continue
				}
				r, err := randomfloat()
				if err != nil {
					client.reportError(err)
					break
				}
				speed := attrs.CredentialType().RevocationUpdateSpeed * 60 * 60
				if r < probability(cred.NonRevocationWitness.Updated, speed) {
					irma.Logger.Debugf("scheduling nonrevocation witness remote update for %s-%d", typ, i)
					client.jobs <- func() {
						if err = client.nonrevUpdateFromServer(typ); err != nil {
							client.reportError(err)
							return
						}
					}
				}
			}
		}
	})
}

// NonrevPrepare updates the revocation state for each credential in the request
// requiring a nonrevocation proof, using the updates included in the request, or the remote
// revocation server if those do not suffice.
func (client *Client) NonrevPrepare(request irma.SessionRequest) error {
	base := request.Base()
	for typ := range request.Disclosure().Identifiers().CredentialTypes {
		credtype := client.Configuration.CredentialTypes[typ]
		if !credtype.RevocationSupported() {
			continue
		}
		if !base.RequestsRevocation(typ) {
			continue
		}
		if err := client.nonrevUpdate(typ, base.Revocation[typ].Updates); err != nil {
			return err
		}
	}
	return nil
}

// nonrevUpdate updates all contained instances of the specified type, using the specified
// updates if present and if they suffice, and contacting the issuer's server to download updates
// otherwise.
func (client *Client) nonrevUpdate(typ irma.CredentialTypeIdentifier, updates map[uint]*revocation.Update) error {
	lowest := map[uint]uint64{}
	attrs := client.attrs(typ)

	// Per credential and issuer key counter we may posess multiple credential instances.
	// Of the nonrevocation witnesses of these, take the lowest index.
	for i := 0; i < len(attrs); i++ {
		cred, err := client.credential(typ, i)
		if err != nil {
			return err
		}
		if cred.NonRevocationWitness == nil {
			continue
		}
		pkid := cred.Pk.Counter
		_, present := lowest[pkid]
		if !present || cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index < lowest[pkid] {
			lowest[pkid] = cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index
		}
	}

	// For each key counter, get an update message starting at the lowest index computed above,
	// that can update all of our credential instance of the given type and key counter,
	// using the specified update messags if they suffice, or the issuer's server otherwise.
	u := map[uint]*revocation.Update{}
	for counter, l := range lowest {
		update := updates[counter]
		if update != nil && len(update.Events) > 0 && update.Events[0].Index <= l+1 {
			u[counter] = update
		} else {
			var err error
			u[counter], err = irma.RevocationClient{Conf: client.Configuration}.
				FetchUpdateFrom(typ, counter, l+1)
			if err != nil {
				return err
			}
		}
	}

	// Apply the update messages to all instances of the given type and key counter
	for counter, update := range u {
		if err := client.nonrevApplyUpdates(typ, counter, update); err != nil {
			return err
		}
	}
	return nil
}

func (client *Client) nonrevApplyUpdates(typ irma.CredentialTypeIdentifier, counter uint, update *revocation.Update) error {
	attrs := client.attrs(typ)
	var save bool
	for i := 0; i < len(attrs); i++ {
		cred, err := client.credential(typ, i)
		if err != nil {
			return err
		}
		if cred.NonRevocationWitness == nil || cred.Pk.Counter != counter {
			continue
		}
		updated, err := cred.nonrevApplyUpdates(update, irma.RevocationKeys{Conf: client.Configuration})
		if updated {
			save = true
		}
		if err == revocation.ErrorRevoked {
			attrs[i].Revoked = true
			cred.attrs.Revoked = true
			save = true
			client.handler.Revoked(&irma.CredentialIdentifier{
				Type: cred.CredentialType().Identifier(),
				Hash: cred.attrs.Hash(),
			})
			// Even if this credential is revoked during a session, we may have
			// other instances that can satisfy the request. So don't return an
			// error which would halt the session.
			continue
		}
		if err != nil {
			return err
		}
	}
	if save {
		if err := client.storage.StoreAttributes(client.attributes); err != nil {
			client.reportError(err)
			return err
		}
	}
	return nil
}

func (client *Client) nonrevUpdateFromServer(typ irma.CredentialTypeIdentifier) error {
	if err := client.nonrevUpdate(typ, map[uint]*revocation.Update{}); err != nil {
		return err
	}
	return nil
}

func (client *Client) nonrevPrepareCache(typ irma.CredentialTypeIdentifier, index int) error {
	irma.Logger.WithFields(logrus.Fields{"credtype": typ, "index": index}).Debug("Preparing cache")
	cred, err := client.credential(typ, index)
	if err != nil {
		return err
	}
	return cred.NonrevPrepareCache()
}

// nonrevRepopulateCaches repopulates the consumed nonrevocation caches of the credentials involved
// in the request, in background jobs, after the request has finished.
func (client *Client) nonrevRepopulateCaches(request irma.SessionRequest) {
	for typ := range request.Disclosure().Identifiers().CredentialTypes {
		credtype := client.Configuration.CredentialTypes[typ]
		if !credtype.RevocationSupported() {
			continue
		}
		for i := range client.attrs(typ) {
			typ := typ
			i := i
			client.jobs <- func() {
				if err := client.nonrevPrepareCache(typ, i); err != nil {
					client.reportError(err)
				}
			}
		}
	}
}

// nonrevApplyUpdates updates the credential's nonrevocation witness using the specified messages,
// if they all verify and if their indices are ahead and adjacent to that of our witness.
func (cred *credential) nonrevApplyUpdates(update *revocation.Update, keys irma.RevocationKeys) (bool, error) {
	oldindex := cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index

	pk, err := keys.PublicKey(cred.CredentialType().IssuerIdentifier(), update.SignedAccumulator.PKCounter)
	if err != nil {
		return false, err
	}
	if err = cred.NonRevocationWitness.Update(pk, update); err != nil {
		return false, err
	}

	return cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index != oldindex, nil
}

// probability returns a float between 0 and asymptote, representing a probability
// that asymptotically increases to the asymptote, reaching
// a reference probability at a reference index.
func probability(lastUpdate time.Time, refindex uint64) float64 {
	const (
		asymptote      = 1.0 / 3          // max probability
		refprobability = 0.75 * asymptote // probability after one week
	)
	f := math.Tan(math.Pi * refprobability / (2 * asymptote))
	i := time.Now().Sub(lastUpdate).Seconds()
	return 2 * asymptote / math.Pi * math.Atan(i/float64(refindex)*f)
}

// randomfloat between 0 and 1
func randomfloat() (float64, error) {
	b := make([]byte, 4)
	_, err := rand.Read(b)
	if err != nil {
		fmt.Println("error:", err)
		return 0, err
	}
	c := float64(binary.BigEndian.Uint32(b)) / float64(^uint32(0)) // random int / max int
	return c, nil
}
<commit_msg>feat: irmaclient updates all relevant witnesses concurrently during a session<commit_after>package irmaclient

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"math"
	"sync"
	"time"

	"github.com/privacybydesign/gabi/revocation"
	irma "github.com/privacybydesign/irmago"
	"github.com/sirupsen/logrus"
)

func (client *Client) initRevocation() {
	// For every credential supporting revocation, compute nonrevocation caches in async jobs
	for typ, attrsets := range client.attributes {
		for i, attrs := range attrsets {
			if attrs.CredentialType() == nil || !attrs.CredentialType().RevocationSupported() {
				continue
			}
			typ := typ // make copy of same name to capture the value for closure below
			i := i     // see https://golang.org/doc/faq#closures_and_goroutines
			client.jobs <- func() {
				if err := client.nonrevPrepareCache(typ, i); err != nil {
					client.reportError(err)
				}
			}
		}
	}

	// Of each credential supporting revocation, we periodically update its nonrevocation witness
	// by fetching updates from the issuer's server, such that:
	// - The time interval between two updates is random so that the server cannot recognize us
	//   using the update interval,
	// - Updating happens regularly even if the app is rarely used.
	// We do this by every 10 seconds updating the credential with a low probability, which
	// increases over time since the last update.
	client.Configuration.Scheduler.Every(irma.RevocationParameters.ClientUpdateInterval).Seconds().Do(func() {
		for typ, attrsets := range client.attributes {
			for i, attrs := range attrsets {
				if attrs.CredentialType() == nil || !attrs.CredentialType().RevocationSupported() {
					continue
				}
				cred, err := client.credential(typ, i)
				if err != nil {
					client.reportError(err)
					continue
				}
				if cred.NonRevocationWitness == nil {
					continue
				}
				r, err := randomfloat()
				if err != nil {
					client.reportError(err)
					break
				}
				speed := attrs.CredentialType().RevocationUpdateSpeed * 60 * 60
				if r < probability(cred.NonRevocationWitness.Updated, speed) {
					irma.Logger.Debugf("scheduling nonrevocation witness remote update for %s-%d", typ, i)
					client.jobs <- func() {
						if err = client.nonrevUpdateFromServer(typ); err != nil {
							client.reportError(err)
							return
						}
					}
				}
			}
		}
	})
}

// NonrevPrepare updates the revocation state for each credential in the request
// requiring a nonrevocation proof, using the updates included in the request, or the remote
// revocation server if those do not suffice.
func (client *Client) NonrevPrepare(request irma.SessionRequest) error {
	base := request.Base()
	var err error
	var wg sync.WaitGroup
	for typ := range request.Disclosure().Identifiers().CredentialTypes {
		credtype := client.Configuration.CredentialTypes[typ]
		if !credtype.RevocationSupported() {
			continue
		}
		if !base.RequestsRevocation(typ) {
			continue
		}
		irma.Logger.WithField("credtype", typ).Debug("updating witnesses")
		wg.Add(1)
		go func() {
			if e := client.nonrevUpdate(typ, base.Revocation[typ].Updates); e != nil {
				err = e // overwrites err from previously finished call, if any
			}
			wg.Done()
		}()
	}
	wg.Wait()
	irma.Logger.Debug("done updating witnesses")
	return err
}

// nonrevUpdate updates all contained instances of the specified type, using the specified
// updates if present and if they suffice, and contacting the issuer's server to download updates
// otherwise.
func (client *Client) nonrevUpdate(typ irma.CredentialTypeIdentifier, updates map[uint]*revocation.Update) error {
	lowest := map[uint]uint64{}
	attrs := client.attrs(typ)

	// Per credential and issuer key counter we may posess multiple credential instances.
	// Of the nonrevocation witnesses of these, take the lowest index.
	for i := 0; i < len(attrs); i++ {
		cred, err := client.credential(typ, i)
		if err != nil {
			return err
		}
		if cred.NonRevocationWitness == nil {
			continue
		}
		pkid := cred.Pk.Counter
		_, present := lowest[pkid]
		if !present || cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index < lowest[pkid] {
			lowest[pkid] = cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index
		}
	}

	// For each key counter, get an update message starting at the lowest index computed above,
	// that can update all of our credential instance of the given type and key counter,
	// using the specified update messags if they suffice, or the issuer's server otherwise.
	u := map[uint]*revocation.Update{}
	for counter, l := range lowest {
		update := updates[counter]
		if update != nil && len(update.Events) > 0 && update.Events[0].Index <= l+1 {
			u[counter] = update
		} else {
			var err error
			u[counter], err = irma.RevocationClient{Conf: client.Configuration}.
				FetchUpdateFrom(typ, counter, l+1)
			if err != nil {
				return err
			}
		}
	}

	// Apply the update messages to all instances of the given type and key counter
	for counter, update := range u {
		if err := client.nonrevApplyUpdates(typ, counter, update); err != nil {
			return err
		}
	}
	return nil
}

func (client *Client) nonrevApplyUpdates(typ irma.CredentialTypeIdentifier, counter uint, update *revocation.Update) error {
	attrs := client.attrs(typ)
	var save bool
	for i := 0; i < len(attrs); i++ {
		cred, err := client.credential(typ, i)
		if err != nil {
			return err
		}
		if cred.NonRevocationWitness == nil || cred.Pk.Counter != counter {
			continue
		}
		updated, err := cred.nonrevApplyUpdates(update, irma.RevocationKeys{Conf: client.Configuration})
		if updated {
			save = true
		}
		if err == revocation.ErrorRevoked {
			attrs[i].Revoked = true
			cred.attrs.Revoked = true
			save = true
			client.handler.Revoked(&irma.CredentialIdentifier{
				Type: cred.CredentialType().Identifier(),
				Hash: cred.attrs.Hash(),
			})
			// Even if this credential is revoked during a session, we may have
			// other instances that can satisfy the request. So don't return an
			// error which would halt the session.
			continue
		}
		if err != nil {
			return err
		}
	}
	if save {
		if err := client.storage.StoreAttributes(client.attributes); err != nil {
			client.reportError(err)
			return err
		}
	}
	return nil
}

func (client *Client) nonrevUpdateFromServer(typ irma.CredentialTypeIdentifier) error {
	if err := client.nonrevUpdate(typ, map[uint]*revocation.Update{}); err != nil {
		return err
	}
	return nil
}

func (client *Client) nonrevPrepareCache(typ irma.CredentialTypeIdentifier, index int) error {
	irma.Logger.WithFields(logrus.Fields{"credtype": typ, "index": index}).Debug("Preparing cache")
	cred, err := client.credential(typ, index)
	if err != nil {
		return err
	}
	return cred.NonrevPrepareCache()
}

// nonrevRepopulateCaches repopulates the consumed nonrevocation caches of the credentials involved
// in the request, in background jobs, after the request has finished.
func (client *Client) nonrevRepopulateCaches(request irma.SessionRequest) {
	for typ := range request.Disclosure().Identifiers().CredentialTypes {
		credtype := client.Configuration.CredentialTypes[typ]
		if !credtype.RevocationSupported() {
			continue
		}
		for i := range client.attrs(typ) {
			typ := typ
			i := i
			client.jobs <- func() {
				if err := client.nonrevPrepareCache(typ, i); err != nil {
					client.reportError(err)
				}
			}
		}
	}
}

// nonrevApplyUpdates updates the credential's nonrevocation witness using the specified messages,
// if they all verify and if their indices are ahead and adjacent to that of our witness.
func (cred *credential) nonrevApplyUpdates(update *revocation.Update, keys irma.RevocationKeys) (bool, error) {
	oldindex := cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index

	pk, err := keys.PublicKey(cred.CredentialType().IssuerIdentifier(), update.SignedAccumulator.PKCounter)
	if err != nil {
		return false, err
	}
	if err = cred.NonRevocationWitness.Update(pk, update); err != nil {
		return false, err
	}

	return cred.NonRevocationWitness.SignedAccumulator.Accumulator.Index != oldindex, nil
}

// probability returns a float between 0 and asymptote, representing a probability
// that asymptotically increases to the asymptote, reaching
// a reference probability at a reference index.
func probability(lastUpdate time.Time, refindex uint64) float64 {
	const (
		asymptote      = 1.0 / 3          // max probability
		refprobability = 0.75 * asymptote // probability after one week
	)
	f := math.Tan(math.Pi * refprobability / (2 * asymptote))
	i := time.Now().Sub(lastUpdate).Seconds()
	return 2 * asymptote / math.Pi * math.Atan(i/float64(refindex)*f)
}

// randomfloat between 0 and 1
func randomfloat() (float64, error) {
	b := make([]byte, 4)
	_, err := rand.Read(b)
	if err != nil {
		fmt.Println("error:", err)
		return 0, err
	}
	c := float64(binary.BigEndian.Uint32(b)) / float64(^uint32(0)) // random int / max int
	return c, nil
}
<|endoftext|>


<commit_before>package zd

import (
	"fmt"
)

type OrganizationWrapper struct {
	Organization *Organization `json:"organization"`
}

type OrganizationListResponse struct {
	Organizations []Organization `json:"organizations"`
	NextPage      string        `json:"next_page,omitempty"`
	PreviousPage  string        `json:"previous_page,omitempty"`
	Count         int           `json:"count,omitempty"`
}

type Organization struct {
	URL                string           `json:"url,omitempty"`
	ID                 int              `json:"id,omitempty"`
	Name               string           `json:"name,omitempty"`
	SharedTickets      bool             `json:"shared_tickets,omitempty"`
	SharedComments     bool             `json:"shared_comments,omitempty"`
	ExternalID         string           `json:"external_id,omitempty"`
	CreatedAt          string           `json:"created_at,omitempty"`
	UpdatedAt          string           `json:"updated_at,omitempty"`
	DomainNames        []string          `json:"domain_names,omitempty"`
	Details            string           `json:"details,omitempty"`
	Notes              string           `json:"notes,omitempty"`
	GroupID            string           `json:"group_id,omitempty"`
	Tags               []string          `json:"tags,omitempty"`
	OrganizationFields map[string]string `json:"organization_fields,omitempty"`
}

// TicketService struct
type OrganizationService struct {
	client *Client
}

// GetOrganizationById finds an organization in zendesk by id
func (s *OrganizationService) GetOrganizationById(organizationID string) (*Organization, *Response, error) {
	org := OrganizationWrapper{}

	url := fmt.Sprintf("organizations/%s.json", organizationID)

	req, err := s.client.NewRequest("GET", url, nil)
	if err != nil {
		return nil, nil, err
	}

	resp, err := s.client.Do(req, &org)
	if err != nil {
		return nil, nil,  err
	}

	return org.Organization, resp, err
}

// UpdateOrganization updates and organization by id
func (s *OrganizationService) UpdateOrganization(org *Organization) (*Organization, error) {
	var organization *Organization
	var err error

	url := fmt.Sprintf("organizations/%v.json", org.ID)

	or := &OrganizationWrapper{Organization: org}

	req, err := s.client.NewRequest("PUT", url, or)
	if err != nil {
		return organization, err
	}

	result := OrganizationWrapper{}

	_, err = s.client.Do(req, result)
	if err != nil {
		return organization, err
	}

	organization = result.Organization

	return organization, err
}

//CreateOrganization creates a new organization
func (s *OrganizationService) CreateOrganization(org *Organization) (*Organization, error) {
	var organization *Organization
	var err error

	or := &OrganizationWrapper{Organization: org}

	url := fmt.Sprintf("organizations.json")

	req, err := s.client.NewRequest("POST", url, or)
	if err != nil {
		return organization, err
	}

	result := OrganizationWrapper{}
	_, err = s.client.Do(req, result)

	if err != nil {
		return organization, err
	}

	organization = result.Organization

	return organization, nil
}
<commit_msg>using fmt<commit_after>package zd

import (
	"fmt"
)

type OrganizationWrapper struct {
	Organization *Organization `json:"organization"`
}

type OrganizationListResponse struct {
	Organizations []Organization `json:"organizations"`
	NextPage      *string        `json:"next_page,omitempty"`
	PreviousPage  *string        `json:"previous_page,omitempty"`
	Count         *int           `json:"count,omitempty"`
}

type Organization struct {
	URL                *string           `json:"url,omitempty"`
	ID                 *int              `json:"id,omitempty"`
	Name               *string           `json:"name,omitempty"`
	SharedTickets      *bool             `json:"shared_tickets,omitempty"`
	SharedComments     *bool             `json:"shared_comments,omitempty"`
	ExternalID         *string           `json:"external_id,omitempty"`
	CreatedAt          *string           `json:"created_at,omitempty"`
	UpdatedAt          *string           `json:"updated_at,omitempty"`
	DomainNames        []string          `json:"domain_names,omitempty"`
	Details            *string           `json:"details,omitempty"`
	Notes              *string           `json:"notes,omitempty"`
	GroupID            *string           `json:"group_id,omitempty"`
	Tags               []string          `json:"tags,omitempty"`
	OrganizationFields map[string]string `json:"organization_fields,omitempty"`
}

// TicketService struct
type OrganizationService struct {
	client *Client
}

// GetOrganizationById finds an organization in zendesk by id
func (s *OrganizationService) GetOrganizationById(organizationID string) (*Organization, *Response, error) {
	org := OrganizationWrapper{}

	url := fmt.Sprintf("organizations/%s.json", organizationID)

	req, err := s.client.NewRequest("GET", url, nil)
	if err != nil {
		return nil, nil, err
	}

	resp, err := s.client.Do(req, &org)
	if err != nil {
		return nil, nil, err
	}

	return org.Organization, resp, err
}

// UpdateOrganization updates and organization by id
func (s *OrganizationService) UpdateOrganization(org *Organization) (*Organization, error) {
	var organization *Organization
	var err error

	url := fmt.Sprintf("organizations/%v.json", org.ID)

	or := &OrganizationWrapper{Organization: org}

	req, err := s.client.NewRequest("PUT", url, or)
	if err != nil {
		return organization, err
	}

	result := OrganizationWrapper{}

	_, err = s.client.Do(req, result)
	if err != nil {
		return organization, err
	}

	organization = result.Organization

	return organization, err
}

//CreateOrganization creates a new organization
func (s *OrganizationService) CreateOrganization(org *Organization) (*Organization, error) {
	var organization *Organization
	var err error

	or := &OrganizationWrapper{Organization: org}

	url := fmt.Sprintf("organizations.json")

	req, err := s.client.NewRequest("POST", url, or)
	if err != nil {
		return organization, err
	}

	result := OrganizationWrapper{}
	_, err = s.client.Do(req, result)

	if err != nil {
		return organization, err
	}

	organization = result.Organization

	return organization, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sort"

	"github.com/phrase/phraseapp-go/phraseapp"
)

func getBaseLocales() []*phraseapp.Locale {
	return []*phraseapp.Locale{
		&phraseapp.Locale{
			Code: "en",
			ID:   "en-locale-id",
			Name: "english",
		},
		&phraseapp.Locale{
			Code: "de",
			ID:   "de-locale-id",
			Name: "german",
		},
	}
}

type ByPath []*LocaleFile

func (a ByPath) Len() int           { return len(a) }
func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPath) Less(i, j int) bool { return a[i].Path < a[j].Path }

func compareLocaleFiles(actualFiles LocaleFiles, expectedFiles LocaleFiles) error {
	sort.Sort(ByPath(actualFiles))
	sort.Sort(ByPath(expectedFiles))
	for idx, actualFile := range actualFiles {
		expected := expectedFiles[idx]
		actual := actualFile
		if expected.Path != actual.Path {
			return fmt.Errorf("Expected Path %s should eql %s", expected.Path, actual.Path)
		}
		if expected.Name != actual.Name {
			return fmt.Errorf("Expected Name %s should eql %s", expected.Name, actual.Name)
		}
		if expected.RFC != actual.RFC {
			return fmt.Errorf("Expected RFC %s should eql %s", expected.RFC, actual.RFC)
		}
		if expected.ID != actual.ID {
			return fmt.Errorf("Expected ID %s should eql %s", expected.ID, actual.ID)
		}
	}
	return nil
}
<commit_msg>tests: add test for default value handling in router<commit_after>package main

import (
	"fmt"
	"sort"

	"github.com/phrase/phraseapp-go/phraseapp"
	"testing"
	"os"
	"io/ioutil"
	"io"
	"bytes"
	"github.com/dynport/dgtk/cli"
	"strings"
	"bufio"
)

func getBaseLocales() []*phraseapp.Locale {
	return []*phraseapp.Locale{
		&phraseapp.Locale{
			Code: "en",
			ID:   "en-locale-id",
			Name: "english",
		},
		&phraseapp.Locale{
			Code: "de",
			ID:   "de-locale-id",
			Name: "german",
		},
	}
}

type ByPath []*LocaleFile

func (a ByPath) Len() int           { return len(a) }
func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPath) Less(i, j int) bool { return a[i].Path < a[j].Path }

func compareLocaleFiles(actualFiles LocaleFiles, expectedFiles LocaleFiles) error {
	sort.Sort(ByPath(actualFiles))
	sort.Sort(ByPath(expectedFiles))
	for idx, actualFile := range actualFiles {
		expected := expectedFiles[idx]
		actual := actualFile
		if expected.Path != actual.Path {
			return fmt.Errorf("Expected Path %s should eql %s", expected.Path, actual.Path)
		}
		if expected.Name != actual.Name {
			return fmt.Errorf("Expected Name %s should eql %s", expected.Name, actual.Name)
		}
		if expected.RFC != actual.RFC {
			return fmt.Errorf("Expected RFC %s should eql %s", expected.RFC, actual.RFC)
		}
		if expected.ID != actual.ID {
			return fmt.Errorf("Expected ID %s should eql %s", expected.ID, actual.ID)
		}
	}
	return nil
}

func captureStderr(f func() error) (string, error) {
	old := os.Stderr // keep backup of the real stdout
	r, w, _ := os.Pipe()
	os.Stderr = w

	outC := make(chan string)
	// copy the output in a separate goroutine so printing can't block indefinitely
	go func(outC chan <- string) {
		defer close(outC)

		buf := bytes.NewBuffer(nil)
		_, _ = io.Copy(buf, r)
		outC <- buf.String()
	}(outC)

	err := f()

	// back to normal state
	w.Close()
	os.Stderr = old // restoring the real stdout

	return <-outC, err
}

func runWithCfg(cfg *phraseapp.Config, cmd string, additionalOpts ...string) (string, error) {
	r, err := router(cfg)
	if err != nil {
		return "", err
	}

	stderr, err := ioutil.TempFile("", "phraseapp_cli_test")
	if err != nil {
		return "", err
	}
	defer os.Remove(stderr.Name())

	opts := strings.Split(cmd, "/")
	opts = append(opts, additionalOpts...)
	opts = append(opts, "-h")

	out, err := captureStderr(func() error {
		return r.Run(opts...)
	})

	if err != cli.ErrorHelpRequested {
		return "", err
	}
	return out, nil
}

func TestCLIHelp_NoDefaults(t *testing.T) {
	cfg := new(phraseapp.Config)

	out, err := runWithCfg(cfg, "locale/download")
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	defaults, err := collectDefaults(out)
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}
	if len(defaults) > 0 {
		t.Errorf("expected no defaults in configuration, found: %#v", defaults)
	}
}

func collectDefaults(out string) (map[string]string, error) {
	res := map[string]string{}

	sc := bufio.NewScanner(bytes.NewBufferString(out))
	for sc.Scan() {
		line := strings.TrimSpace(sc.Text())
		if !strings.Contains(line, "(default:") {
			continue
		}
		fields := strings.Split(line, " ")

		if name, ok := searchFieldWithPrefix(fields, hasPrefix("--")); ok {
			if value, ok := searchFieldWithPredecessor(fields, "(default:"); ok {
				res[name] = value
			}
		}

	}
	return res, sc.Err()
}

func searchFieldWithPredecessor(flds []string, pre string) (string, bool) {
	i := findInStringList(flds, func(s string) bool {
		return s == pre
	})

	switch i {
	case -1:
		return "", false
	case len(flds) - 1:
		return "", false
	default:
		return strings.Trim(flds[i + 1], ")"), true
	}

}

func searchFieldWithPrefix(flds []string, pred func(string) bool) (string, bool) {
	switch i := findInStringList(flds, pred); i {
	case -1:
		return "", false
	default:
		return flds[i], true
	}
}

func hasPrefix(p string) func(s string) bool {
	return func(s string) bool {
		return strings.HasPrefix(s, p)
	}
}

func findInStringList(l []string, f func(string) bool) int {
	for i := range l {
		if f(l[i]) {
			return i
		}
	}
	return -1
}

func matchDefaultExpectations(t *testing.T, got, exp map[string]string) {
	for k, v := range exp {
		switch gv, found := got[k]; {
		case found && gv != v:
			t.Errorf("%s: expected value %q, got %q", k, v, gv)
		case !found:
			t.Errorf("%s: expected to be present, it wasn't", k)
		}
		delete(got, k)
	}

	for k, _ := range got {
		t.Errorf("%s: is present, but wasn't expected", k)
	}
}

func TestCLIHelp_FileFormatDefault(t *testing.T) {
	cfg := new(phraseapp.Config)
	cfg.DefaultFileFormat = "FILE_FORMAT"

	out, err := runWithCfg(cfg, "locale/download")
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	defaults, err := collectDefaults(out)
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	matchDefaultExpectations(t, defaults, map[string]string{
		"--file-format": "FILE_FORMAT",
	})
}

func TestCLIHelp_FileFormatDefaultTwice(t *testing.T) {
	cfg := new(phraseapp.Config)
	cfg.DefaultFileFormat = "FILE_FORMAT"
	cfg.Defaults = map[string]map[string]interface{}{}
	cfg.Defaults["locale/download"] = map[string]interface{}{
		"file_format": "OTHER_FILE_FORMAT",
	}

	out, err := runWithCfg(cfg, "locale/download")
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	defaults, err := collectDefaults(out)
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	matchDefaultExpectations(t, defaults, map[string]string{
		"--file-format": "OTHER_FILE_FORMAT",
	})
}

func TestCLIHelp_FileFormatDefaultThrice(t *testing.T) {
	cfg := new(phraseapp.Config)
	cfg.DefaultFileFormat = "FILE_FORMAT"
	cfg.Defaults = map[string]map[string]interface{}{}
	cfg.Defaults["locale/download"] = map[string]interface{}{
		"file_format": "OTHER_FILE_FORMAT",
	}

	out, err := runWithCfg(cfg, "locale/download", "--file-format", "YET_ANOTHER_FILE_FORMAT")
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	defaults, err := collectDefaults(out)
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	matchDefaultExpectations(t, defaults, map[string]string{
		"--file-format": "YET_ANOTHER_FILE_FORMAT",
	})
}

func itop(i int) *int {
	return &i
}

func TestCLIHelp_PerPageSettings(t *testing.T) {
	cfg := new(phraseapp.Config)
	cfg.Page = itop(2)
	cfg.PerPage = itop(12)

	out, err := runWithCfg(cfg, "locales/list")
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	defaults, err := collectDefaults(out)
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	matchDefaultExpectations(t, defaults, map[string]string{
		"--page": "2",
		"--per-page": "12",
	})
}

func TestCLIHelp_PerPageSettingsOverride(t *testing.T) {
	cfg := new(phraseapp.Config)
	cfg.Page = itop(2)
	cfg.PerPage = itop(12)

	out, err := runWithCfg(cfg, "locales/list", "--page", "3", "--per-page", "42")
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	defaults, err := collectDefaults(out)
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	matchDefaultExpectations(t, defaults, map[string]string{
		"--page": "3",
		"--per-page": "42",
	})
}

func TestCLIHelp_FormatOptions(t *testing.T) {
	cfg := new(phraseapp.Config)
	cfg.Defaults = map[string]map[string]interface{}{}
	cfg.Defaults["locale/download"] = map[string]interface{}{
		"format_options": map[interface{}]interface{}{
			"convert_placeholders": "true",
		},
	}

	out, err := runWithCfg(cfg, "locale/download")
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	defaults, err := collectDefaults(out)
	if err != nil {
		t.Fatalf("didn't expect an error, got: %s", err)
	}

	// FormatOptions are ignored with regard to defaults!
	matchDefaultExpectations(t, defaults, map[string]string{})
}<|endoftext|>


<commit_before>package main

import (
	"github.com/minotar/minecraft"
	"testing"
)

func TestNothing(t *testing.T) {

}

func BenchmarkGetHead(b *testing.B) {
	skin := fetchSkin("clone1018")
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		GetHead(skin)
	}
}

func BenchmarkGetHelm(b *testing.B) {
	skin := fetchSkin("clone1018")
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		GetHelm(skin)
	}
}

func BenchmarkGetBody(b *testing.B) {
	skin := fetchSkin("clone1018")
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		GetBody(skin)
	}
}
<commit_msg>Remove an unused import<commit_after>package main

import (
	"testing"
)

func TestNothing(t *testing.T) {

}

func BenchmarkGetHead(b *testing.B) {
	skin := fetchSkin("clone1018")
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		GetHead(skin)
	}
}

func BenchmarkGetHelm(b *testing.B) {
	skin := fetchSkin("clone1018")
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		GetHelm(skin)
	}
}

func BenchmarkGetBody(b *testing.B) {
	skin := fetchSkin("clone1018")
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		GetBody(skin)
	}
}
<|endoftext|>


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

package main

import (
	"testing"
)

func TestMainStart(t *testing.T) {
	router, err := MainStart(nil, NewUUID(), ":1000",
		"bad data dir", "./static", "", false)
	if router != nil || err == nil {
		t.Errorf("expected empty server string to fail mainStart()")
	}

	router, err = MainStart(nil, NewUUID(), ":1000",
		"bad data dir", "./static", "bad server", false)
	if router != nil || err == nil {
		t.Errorf("expected bad server string to fail mainStart()")
	}
}
<commit_msg>added TestMainUUID()<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package main

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

func TestMainStart(t *testing.T) {
	router, err := MainStart(nil, NewUUID(), ":1000",
		"bad data dir", "./static", "", false)
	if router != nil || err == nil {
		t.Errorf("expected empty server string to fail mainStart()")
	}

	router, err = MainStart(nil, NewUUID(), ":1000",
		"bad data dir", "./static", "bad server", false)
	if router != nil || err == nil {
		t.Errorf("expected bad server string to fail mainStart()")
	}
}

func TestMainUUID(t *testing.T) {
	emptyDir, _ := ioutil.TempDir("./tmp", "test")
	defer os.RemoveAll(emptyDir)

	uuid, err := MainUUID(emptyDir)
	if err != nil || uuid == "" {
		t.Errorf("expected MainUUID() to work, err: %v", err)
	}

	uuid2, err := MainUUID(emptyDir)
	if err != nil || uuid2 != uuid {
		t.Errorf("expected MainUUID() reload to give same uuid, uuid: %s vs %s, err: %v",
			uuid, uuid2, err)
	}

	path := emptyDir + string(os.PathSeparator) + "cbft.uuid"
	os.Remove(path)
	ioutil.WriteFile(path, []byte{}, 0600)

	uuid3, err := MainUUID(emptyDir)
	if err == nil || uuid3 != "" {
		t.Errorf("expected MainUUID() to fail on empty file")
	}
}
<|endoftext|>


<commit_before>package boardgame

//Place to define testing structs and helpers that are useful throughout

//testingComponent is a very basic thing that fufills the Component interface.
type testingComponent struct {
	deckName  string
	deckIndex int
	String    string
	Integer   int
}

const testGameName = "testgame"

func (t *testingComponent) Props() []string {
	return []string{"String", "Integer"}
}

func (t *testingComponent) Prop(name string) interface{} {
	switch name {
	case "String":
		return t.String
	case "Integer":
		return t.Integer
	default:
		return nil
	}
}

func (t *testingComponent) Deck() string {
	return t.deckName
}

func (t *testingComponent) DeckIndex() int {
	return t.deckIndex
}

func (t *testingComponent) GameName() string {
	return testGameName
}

func componentsEqual(one Component, two Component) bool {
	if one == nil && two == nil {
		return true
	}
	if one == nil || two == nil {
		return false
	}
	if one.Deck() != two.Deck() {
		return false
	}
	if one.DeckIndex() != two.DeckIndex() {
		return false
	}
	return true
}

type testStatePayload struct {
	game  *testGameState
	users []*testUserState
}

func (t *testStatePayload) Game() GameState {
	return t.game
}

func (t *testStatePayload) Users() []UserState {
	result := make([]UserState, len(t.users))
	for i, user := range t.users {
		result[i] = user
	}
	return result
}

func (t *testStatePayload) Copy() StatePayload {

	result := &testStatePayload{}

	if t.game != nil {
		result.game = t.game.Copy().(*testGameState)
	}

	if t.users == nil {
		return result
	}

	array := make([]*testUserState, len(t.users))
	for i, user := range t.users {
		array[i] = user.Copy().(*testUserState)
	}
	result.users = array

	return result
}

func (t *testStatePayload) JSON() JSONObject {

	usersArray := make([]JSONObject, len(t.users))

	for i, user := range t.users {
		usersArray[i] = user.JSON()
	}

	return JSONMap{
		"Game":  t.game.JSON(),
		"Users": usersArray,
	}
}

type testGameState struct {
	CurrentPlayer int
	//TODO: have a Stack here.
}

func (t *testGameState) Copy() GameState {
	return &testGameState{
		CurrentPlayer: t.CurrentPlayer,
	}
}

func (t *testGameState) JSON() JSONObject {
	//TODO: once JSONObject is more generic, we can just return ourselves

	return JSONMap{
		"CurrentPlayer": t.CurrentPlayer,
	}
}

func (t *testGameState) Props() []string {
	return []string{"CurrentPlayer"}
}

func (t *testGameState) Prop(name string) interface{} {
	switch name {
	case "CurrentPlayer":
		return t.CurrentPlayer
	default:
		return nil
	}
}

type testUserState struct {
	//Note: PlayerIndex is stored ehre, but not a normal property or
	//serialized, because it's really just a convenience method because it's
	//implied by its position in the State.Users array.
	playerIndex int
	Score       int
	IsFoo       bool
}

func (t *testUserState) PlayerIndex() int {
	return t.playerIndex
}

func (t *testUserState) Copy() UserState {
	return &testUserState{
		playerIndex: t.playerIndex,
		Score:       t.Score,
		IsFoo:       t.IsFoo,
	}
}

func (t *testUserState) JSON() JSONObject {
	return JSONMap{
		"Score": t.Score,
		"IsFoo": t.IsFoo,
	}
}

func (t *testUserState) Props() []string {
	return []string{"Score", "IsFoo"}
}

func (t *testUserState) Prop(name string) interface{} {
	switch name {
	case "Score":
		return t.Score
	case "IsFoo":
		return t.IsFoo
	default:
		return nil
	}
}

type testMove struct {
	AString        string
	ScoreIncrement int
	ABool          bool
}

func (t *testMove) GameName() string {
	return testGameName
}

func (t *testMove) Props() []string {
	return []string{"AString", "ScoreIncrement", "ABool"}
}

func (t *testMove) Prop(name string) interface{} {
	switch name {
	case "AString":
		return t.AString
	case "ScoreIncrement":
		return t.ScoreIncrement
	case "ABool":
		return t.ABool
	default:
		return nil
	}
}

func (t *testMove) JSON() JSONObject {
	return JSONMap{
		"AString":        t.AString,
		"ScoreIncrement": t.ScoreIncrement,
		"ABool":          t.ABool,
	}
}

func (t *testMove) Legal(state *State) bool {

	payload := state.Payload.(*testStatePayload)

	if payload.game.CurrentPlayer != 0 {
		return false
	}

	return true

}

func (t *testMove) Apply(state *State) *State {
	result := state.Copy()

	payload := result.Payload.(*testStatePayload)

	payload.game.CurrentPlayer++
	payload.users[1].Score += t.ScoreIncrement

	return result
}

func testGame() *Game {
	//TODO: some kind of way to set the deckName/Index automatically at insertion?
	chest := ComponentChest{
		"test": &Deck{
			Name: "test",
			Components: []Component{
				&testingComponent{
					"test",
					0,
					"foo",
					1,
				},
				&testingComponent{
					"test",
					1,
					"bar",
					2,
				},
			},
		},
	}

	game := &Game{
		testGameName,
		chest,
		&State{
			Version: 0,
			Schema:  0,
			Payload: &testStatePayload{
				game: &testGameState{
					CurrentPlayer: 0,
				},
				users: []*testUserState{
					&testUserState{
						playerIndex: 0,
						Score:       0,
						IsFoo:       false,
					},
					&testUserState{
						playerIndex: 1,
						Score:       0,
						IsFoo:       false,
					},
					&testUserState{
						playerIndex: 2,
						Score:       0,
						IsFoo:       true,
					},
				},
			},
		},
	}

	return game
}
<commit_msg>Instead of making a JSONMap, just return self in more cases. Fixes #6.<commit_after>package boardgame

//Place to define testing structs and helpers that are useful throughout

//testingComponent is a very basic thing that fufills the Component interface.
type testingComponent struct {
	deckName  string
	deckIndex int
	String    string
	Integer   int
}

const testGameName = "testgame"

func (t *testingComponent) Props() []string {
	return []string{"String", "Integer"}
}

func (t *testingComponent) Prop(name string) interface{} {
	switch name {
	case "String":
		return t.String
	case "Integer":
		return t.Integer
	default:
		return nil
	}
}

func (t *testingComponent) Deck() string {
	return t.deckName
}

func (t *testingComponent) DeckIndex() int {
	return t.deckIndex
}

func (t *testingComponent) GameName() string {
	return testGameName
}

func componentsEqual(one Component, two Component) bool {
	if one == nil && two == nil {
		return true
	}
	if one == nil || two == nil {
		return false
	}
	if one.Deck() != two.Deck() {
		return false
	}
	if one.DeckIndex() != two.DeckIndex() {
		return false
	}
	return true
}

type testStatePayload struct {
	game  *testGameState
	users []*testUserState
}

func (t *testStatePayload) Game() GameState {
	return t.game
}

func (t *testStatePayload) Users() []UserState {
	result := make([]UserState, len(t.users))
	for i, user := range t.users {
		result[i] = user
	}
	return result
}

func (t *testStatePayload) Copy() StatePayload {

	result := &testStatePayload{}

	if t.game != nil {
		result.game = t.game.Copy().(*testGameState)
	}

	if t.users == nil {
		return result
	}

	array := make([]*testUserState, len(t.users))
	for i, user := range t.users {
		array[i] = user.Copy().(*testUserState)
	}
	result.users = array

	return result
}

func (t *testStatePayload) JSON() JSONObject {

	usersArray := make([]JSONObject, len(t.users))

	for i, user := range t.users {
		usersArray[i] = user.JSON()
	}

	return JSONMap{
		"Game":  t.game.JSON(),
		"Users": usersArray,
	}
}

type testGameState struct {
	CurrentPlayer int
	//TODO: have a Stack here.
}

func (t *testGameState) Copy() GameState {
	return &testGameState{
		CurrentPlayer: t.CurrentPlayer,
	}
}

func (t *testGameState) JSON() JSONObject {

	return t
}

func (t *testGameState) Props() []string {
	return []string{"CurrentPlayer"}
}

func (t *testGameState) Prop(name string) interface{} {
	switch name {
	case "CurrentPlayer":
		return t.CurrentPlayer
	default:
		return nil
	}
}

type testUserState struct {
	//Note: PlayerIndex is stored ehre, but not a normal property or
	//serialized, because it's really just a convenience method because it's
	//implied by its position in the State.Users array.
	playerIndex int
	Score       int
	IsFoo       bool
}

func (t *testUserState) PlayerIndex() int {
	return t.playerIndex
}

func (t *testUserState) Copy() UserState {
	return &testUserState{
		playerIndex: t.playerIndex,
		Score:       t.Score,
		IsFoo:       t.IsFoo,
	}
}

func (t *testUserState) JSON() JSONObject {
	return t
}

func (t *testUserState) Props() []string {
	return []string{"Score", "IsFoo"}
}

func (t *testUserState) Prop(name string) interface{} {
	switch name {
	case "Score":
		return t.Score
	case "IsFoo":
		return t.IsFoo
	default:
		return nil
	}
}

type testMove struct {
	AString        string
	ScoreIncrement int
	ABool          bool
}

func (t *testMove) GameName() string {
	return testGameName
}

func (t *testMove) Props() []string {
	return []string{"AString", "ScoreIncrement", "ABool"}
}

func (t *testMove) Prop(name string) interface{} {
	switch name {
	case "AString":
		return t.AString
	case "ScoreIncrement":
		return t.ScoreIncrement
	case "ABool":
		return t.ABool
	default:
		return nil
	}
}

func (t *testMove) JSON() JSONObject {
	return t
}

func (t *testMove) Legal(state *State) bool {

	payload := state.Payload.(*testStatePayload)

	if payload.game.CurrentPlayer != 0 {
		return false
	}

	return true

}

func (t *testMove) Apply(state *State) *State {
	result := state.Copy()

	payload := result.Payload.(*testStatePayload)

	payload.game.CurrentPlayer++
	payload.users[1].Score += t.ScoreIncrement

	return result
}

func testGame() *Game {
	//TODO: some kind of way to set the deckName/Index automatically at insertion?
	chest := ComponentChest{
		"test": &Deck{
			Name: "test",
			Components: []Component{
				&testingComponent{
					"test",
					0,
					"foo",
					1,
				},
				&testingComponent{
					"test",
					1,
					"bar",
					2,
				},
			},
		},
	}

	game := &Game{
		testGameName,
		chest,
		&State{
			Version: 0,
			Schema:  0,
			Payload: &testStatePayload{
				game: &testGameState{
					CurrentPlayer: 0,
				},
				users: []*testUserState{
					&testUserState{
						playerIndex: 0,
						Score:       0,
						IsFoo:       false,
					},
					&testUserState{
						playerIndex: 1,
						Score:       0,
						IsFoo:       false,
					},
					&testUserState{
						playerIndex: 2,
						Score:       0,
						IsFoo:       true,
					},
				},
			},
		},
	}

	return game
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"reflect"
	"sort"
	"strings"
	"testing"
	"time"
)

func TestScanToChannel(t *testing.T) {
	lines := `one
two`

	from := strings.NewReader(lines)
	to := make(chan string, 2)
	cancel := make(chan struct{})

	scanToChannel(from, to, cancel)

	log.Println("1")
	line1 := <-to
	log.Println("2")
	line2 := <-to
	log.Println("3")

	if line1 != "one" {
		t.Error("first line wasn't 'one'")
	}
	if line2 != "two" {
		t.Error("second line wasn't 'two'")
	}
}

func TestReadCmd(t *testing.T) {
	cmdString := `echo -e "one\ntwo"`
	o := make(chan string, 2)
	cancel := make(chan struct{})

	readCmd(cmdString, o, cancel)

	line1 := <-o
	line2 := <-o

	if line1 != "one" {
		t.Error("first line wasn't 'one'")
	}
	if line2 != "two" {
		t.Error("second line wasn't 'two'")
	}
}

type mockUtils struct{}

func (m mockUtils) scanStdinToChannel(i chan string) {}

func TestDiff(t *testing.T) {
	i := make(chan string)
	stdout := make(chan string)

	go diff(`echo -e "one\ntwo"`, 100*time.Millisecond, i, stdout, mockUtils{})
	i <- "one"
	i <- "two"
	i <- "three"
	i <- "four"

	lines := []string{}
loop:
	for {
		select {
		case line, ok := <-stdout:
			if !ok {
				break loop
			}
			lines = append(lines, line)
		case <-time.After(1 * time.Second):
			break loop
		}
	}

	sort.Strings(lines) // order is not deterministic

	if reflect.DeepEqual(lines, []string{"four", "three"}) != true {
		t.Errorf("lines weren't ['four', 'three'], it was %v", lines)
	}
}

func TestDiffWhenInputTimesOut(t *testing.T) {
	i := make(chan string)
	stdout := make(chan string)

	go diff(`echo -e "one\ntwo"`, 100*time.Millisecond, i, stdout, mockUtils{})

	go func() {
		i <- "one"
		i <- "three"
		i <- "three"
		i <- "three"
		i <- "one"
		i <- "two"
		i <- "four"
		time.Sleep(101 * time.Millisecond)
		i <- "five"
	}()

	lines := []string{}
loop:
	for {
		select {
		case line, ok := <-stdout:
			if !ok {
				break loop
			}
			lines = append(lines, line)
		case <-time.After(1 * time.Second):
			break loop
		}
	}

	sort.Strings(lines) // order is not deterministic
	if reflect.DeepEqual(lines, []string{"four", "three", "three", "three"}) != true {
		t.Errorf("result wasn't ['four', 'three', 'three', 'three'], it was %v", lines)
	}
}

func TestDiffWhenOutputTimesOut(t *testing.T) {
	i := make(chan string)
	stdout := make(chan string)

	go diff(`echo -e "one\ntwo" && sleep 1 && echo -e "three\nfour"`, 100*time.Millisecond, i, stdout, mockUtils{})

	i <- "one"
	i <- "two"
	i <- "three"
	i <- "four"
	i <- "five"

	lines := []string{}
loop:
	for {
		select {
		case line, ok := <-stdout:
			if !ok {
				break loop
			}
			lines = append(lines, line)
		case <-time.After(1 * time.Second):
			break loop
		}
	}

	sort.Strings(lines) // order is not deterministic
	if reflect.DeepEqual(lines, []string{"five", "four", "three"}) != true {
		t.Errorf("result wasn't ['five', 'four', 'three'], it was %v", lines)
	}
}
<commit_msg>Tests DiffWhenDelaysAddUpToTimeoutSeparatelyButDoesntTimeout<commit_after>package main

import (
	"reflect"
	"sort"
	"strings"
	"testing"
	"time"
)

func TestScanToChannel(t *testing.T) {
	lines := `one
two`

	from := strings.NewReader(lines)
	to := make(chan string, 2)
	cancel := make(chan struct{})

	scanToChannel(from, to, cancel)

	line1 := <-to
	line2 := <-to

	if line1 != "one" {
		t.Error("first line wasn't 'one'")
	}
	if line2 != "two" {
		t.Error("second line wasn't 'two'")
	}
}

func TestReadCmd(t *testing.T) {
	cmdString := `echo -e "one\ntwo"`
	o := make(chan string, 2)
	cancel := make(chan struct{})

	readCmd(cmdString, o, cancel)

	line1 := <-o
	line2 := <-o

	if line1 != "one" {
		t.Error("first line wasn't 'one'")
	}
	if line2 != "two" {
		t.Error("second line wasn't 'two'")
	}
}

type mockUtils struct{}

func (m mockUtils) scanStdinToChannel(i chan string) {}

func TestDiff(t *testing.T) {
	i := make(chan string)
	stdout := make(chan string)

	go diff(`echo -e "one\ntwo"`, 100*time.Millisecond, i, stdout, mockUtils{})
	i <- "one"
	i <- "two"
	i <- "three"
	i <- "four"

	lines := []string{}
loop:
	for {
		select {
		case line, ok := <-stdout:
			if !ok {
				break loop
			}
			lines = append(lines, line)
		case <-time.After(1 * time.Second):
			break loop
		}
	}

	sort.Strings(lines) // order is not deterministic

	if reflect.DeepEqual(lines, []string{"four", "three"}) != true {
		t.Errorf("lines weren't ['four', 'three'], it was %v", lines)
	}
}

func TestDiffWhenInputTimesOut(t *testing.T) {
	i := make(chan string)
	stdout := make(chan string)

	go diff(`echo -e "one\ntwo"`, 100*time.Millisecond, i, stdout, mockUtils{})

	go func() {
		i <- "one"
		i <- "three"
		i <- "three"
		i <- "three"
		i <- "one"
		i <- "two"
		i <- "four"
		time.Sleep(101 * time.Millisecond)
		i <- "five"
	}()

	lines := []string{}
loop:
	for {
		select {
		case line, ok := <-stdout:
			if !ok {
				break loop
			}
			lines = append(lines, line)
		case <-time.After(1 * time.Second):
			break loop
		}
	}

	sort.Strings(lines) // order is not deterministic
	if reflect.DeepEqual(lines, []string{"four", "three", "three", "three"}) != true {
		t.Errorf("result wasn't ['four', 'three', 'three', 'three'], it was %v", lines)
	}
}

func TestDiffWhenOutputTimesOut(t *testing.T) {
	i := make(chan string)
	stdout := make(chan string)

	go diff(`echo -e "one\ntwo" && sleep 1 && echo -e "three\nfour"`, 100*time.Millisecond, i, stdout, mockUtils{})

	i <- "one"
	i <- "two"
	i <- "three"
	i <- "four"
	i <- "five"

	lines := []string{}
loop:
	for {
		select {
		case line, ok := <-stdout:
			if !ok {
				break loop
			}
			lines = append(lines, line)
		case <-time.After(1 * time.Second):
			break loop
		}
	}

	sort.Strings(lines) // order is not deterministic
	if reflect.DeepEqual(lines, []string{"five", "four", "three"}) != true {
		t.Errorf("result wasn't ['five', 'four', 'three'], it was %v", lines)
	}
}

func TestDiffWhenDelaysAddUpToTimeoutSeparatelyButDoesntTimeout(t *testing.T) {
	i := make(chan string)
	stdout := make(chan string)

	go diff(`echo "one" && sleep .1 && echo "two" && sleep .1 && echo "three" && sleep .1 && echo "four" && sleep .1 && echo "ten"`,
		200*time.Millisecond, i, stdout, mockUtils{})

	i <- "one"
	i <- "two"
	i <- "five"
	i <- "three"
	i <- "four"
	i <- "six"

	lines := []string{}
loop:
	for {
		select {
		case line, ok := <-stdout:
			if !ok {
				break loop
			}
			lines = append(lines, line)
		case <-time.After(1 * time.Second):
			break loop
		}
	}
	sort.Strings(lines) // order is not deterministic

	if reflect.DeepEqual(lines, []string{"five", "six"}) != true {
		t.Errorf("result wasn't ['five', 'six'], it was %v", lines)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"testing"
	//"os/exec"
	//"runtime"
)

var message = "initial"
var count = 0

func set(mes string) {
	message = mes
	count = 0
}
func eq(t *testing.T, actual interface{}, expected interface{}) {
	count++
	if actual != expected {
		fmt.Println(message, count)
		t.Errorf("got %v\nwant %v", actual, expected)
	}
}

func TestMain(m *testing.M) {
  code:=m.Run()
  ox.Exit(code)
}
}

func TestDir(t *testing.T) {
  Dir(".","/tmp")
}
<commit_msg>Delete unuse function<commit_after>package main

import (
	"fmt"
	"os"
	"testing"
)

var message = "initial"
var count = 0

func set(mes string) {
	message = mes
	count = 0
}
func eq(t *testing.T, actual interface{}, expected interface{}) {
	count++
	if actual != expected {
		fmt.Println(message, count)
		t.Errorf("got %v\nwant %v", actual, expected)
	}
}

func TestMain(m *testing.M) {
	code := m.Run()
	os.Exit(code)
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru 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 main

import (
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"github.com/tsuru/tsuru/provision"
	"github.com/tsuru/tsuru/testing"
	"launchpad.net/gocheck"
)

func (s *S) TestAppListIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru")
	list, ok := manager.Commands["app-list"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(list, gocheck.FitsTypeOf, tsuru.AppList{})
}

func (s *S) TestSetCNameIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	cname, ok := manager.Commands["set-cname"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(cname, gocheck.FitsTypeOf, &tsuru.SetCName{})
}

func (s *S) TestUnsetCNameIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	cname, ok := manager.Commands["unset-cname"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(cname, gocheck.FitsTypeOf, &tsuru.UnsetCName{})
}

func (s *S) TestTokenGenIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	token, ok := manager.Commands["token-gen"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(token, gocheck.FitsTypeOf, &tokenGen{})
}

func (s *S) TestLogRemoveIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	token, ok := manager.Commands["log-remove"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(token, gocheck.FitsTypeOf, &logRemove{})
}

func (s *S) TestChangeQuotaIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	token, ok := manager.Commands["quota-update"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(token, gocheck.FitsTypeOf, &changeQuota{})
}

func (s *S) TestMoveContainerIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	token, ok := manager.Commands["containers-move"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(token, gocheck.FitsTypeOf, &moveContainer{})
}

func (s *S) TestCommandsFromBaseManagerAreRegistered(c *gocheck.C) {
	baseManager := cmd.BuildBaseManager("tsuru", version, header, nil)
	manager := buildManager("tsuru")
	for name, instance := range baseManager.Commands {
		command, ok := manager.Commands[name]
		c.Assert(ok, gocheck.Equals, true)
		c.Assert(command, gocheck.FitsTypeOf, instance)
	}
}

func (s *S) TestShouldRegisterAllCommandsFromProvisioners(c *gocheck.C) {
	fp := testing.NewFakeProvisioner()
	p := testing.AdminCommandableProvisioner{FakeProvisioner: *fp}
	provision.Register("fakeAdminProvisioner", &p)
	manager := buildManager("tsuru-admin")
	fake, ok := manager.Commands["fake-admin"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(fake, gocheck.FitsTypeOf, &testing.FakeAdminCommand{})
}
<commit_msg>cmd/tsuru-admin: Removed now invalid test<commit_after>// Copyright 2014 tsuru 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 main

import (
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"github.com/tsuru/tsuru/provision"
	"github.com/tsuru/tsuru/testing"
	"launchpad.net/gocheck"
)

func (s *S) TestAppListIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru")
	list, ok := manager.Commands["app-list"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(list, gocheck.FitsTypeOf, tsuru.AppList{})
}

func (s *S) TestSetCNameIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	cname, ok := manager.Commands["set-cname"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(cname, gocheck.FitsTypeOf, &tsuru.SetCName{})
}

func (s *S) TestUnsetCNameIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	cname, ok := manager.Commands["unset-cname"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(cname, gocheck.FitsTypeOf, &tsuru.UnsetCName{})
}

func (s *S) TestTokenGenIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	token, ok := manager.Commands["token-gen"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(token, gocheck.FitsTypeOf, &tokenGen{})
}

func (s *S) TestLogRemoveIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	token, ok := manager.Commands["log-remove"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(token, gocheck.FitsTypeOf, &logRemove{})
}

func (s *S) TestChangeQuotaIsRegistered(c *gocheck.C) {
	manager := buildManager("tsuru-admin")
	token, ok := manager.Commands["quota-update"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(token, gocheck.FitsTypeOf, &changeQuota{})
}

func (s *S) TestCommandsFromBaseManagerAreRegistered(c *gocheck.C) {
	baseManager := cmd.BuildBaseManager("tsuru", version, header, nil)
	manager := buildManager("tsuru")
	for name, instance := range baseManager.Commands {
		command, ok := manager.Commands[name]
		c.Assert(ok, gocheck.Equals, true)
		c.Assert(command, gocheck.FitsTypeOf, instance)
	}
}

func (s *S) TestShouldRegisterAllCommandsFromProvisioners(c *gocheck.C) {
	fp := testing.NewFakeProvisioner()
	p := testing.AdminCommandableProvisioner{FakeProvisioner: *fp}
	provision.Register("fakeAdminProvisioner", &p)
	manager := buildManager("tsuru-admin")
	fake, ok := manager.Commands["fake-admin"]
	c.Assert(ok, gocheck.Equals, true)
	c.Assert(fake, gocheck.FitsTypeOf, &testing.FakeAdminCommand{})
}
<|endoftext|>


<commit_before>// Package histogram provides a Go implementation of BigML's histogram package
// for Clojure/Java. It is currently experiemental.
package histogram

import (
	"container/heap"
	"math"
	"sort"
)

type Bin struct {
	Count int
	Sum   float64
}

func (b *Bin) Update(x *Bin) {
	b.Count += x.Count
	b.Sum += x.Sum
}

func (b *Bin) Mean() float64 {
	return b.Sum / float64(b.Count)
}

type Bins []*Bin

func (bs Bins) Len() int           { return len(bs) }
func (bs Bins) Less(i, j int) bool { return bs[i].Mean() < bs[j].Mean() }
func (bs Bins) Swap(i, j int)      { bs[i], bs[j] = bs[j], bs[i] }

func (bs *Bins) Push(x interface{}) {
	*bs = append(*bs, x.(*Bin))
}

func (bs *Bins) Pop() interface{} {
	return bs.remove(len(*bs) - 1)
}

func (bs *Bins) remove(n int) *Bin {
	a := *bs
	if n < 0 || len(a) < n {
		return nil
	}
	x := a[n]
	*bs = append(a[:n], a[n+1:]...)
	return x
}

type Histogram struct {
	res *reservoir
}

func New(maxBins int) *Histogram {
	return &Histogram{res: newReservoir(maxBins)}
}

func (h *Histogram) Insert(f float64) {
	h.res.insert(&Bin{1, f})
	h.res.compress()
}

func (h *Histogram) Bins() Bins {
	return h.res.bins
}

type reservoir struct {
	n       int
	maxBins int
	bins    Bins
}

func newReservoir(maxBins int) *reservoir {
	return &reservoir{maxBins: maxBins}
}

func (r *reservoir) insert(bin *Bin) {
	r.n += bin.Count
	i := sort.Search(len(r.bins), func(i int) bool {
		return r.bins[i].Mean() >= bin.Mean()
	})
	if i < 0 || i == r.bins.Len() {
		heap.Push(&r.bins, bin)
		return
	}
	r.bins[i].Update(bin)
}

func (r *reservoir) compress() {
	for r.bins.Len() > r.maxBins {
		minGapIndex := -1
		minGap := math.MaxFloat64
		for i := 0; i < r.bins.Len()-1; i++ {
			gap := gapWeight(r.bins[i], r.bins[i+1])
			if minGap > gap {
				minGap = gap
				minGapIndex = i
			}
		}
		prev := r.bins[minGapIndex]
		next := r.bins.remove(minGapIndex + 1)
		prev.Update(next)
	}
}

func gapWeight(prev, next *Bin) float64 {
	return next.Mean() - prev.Mean()
}
<commit_msg>histogram: TODO about reducing complexity on insert<commit_after>// Package histogram provides a Go implementation of BigML's histogram package
// for Clojure/Java. It is currently experiemental.
package histogram

import (
	"container/heap"
	"math"
	"sort"
)

type Bin struct {
	Count int
	Sum   float64
}

func (b *Bin) Update(x *Bin) {
	b.Count += x.Count
	b.Sum += x.Sum
}

func (b *Bin) Mean() float64 {
	return b.Sum / float64(b.Count)
}

type Bins []*Bin

func (bs Bins) Len() int           { return len(bs) }
func (bs Bins) Less(i, j int) bool { return bs[i].Mean() < bs[j].Mean() }
func (bs Bins) Swap(i, j int)      { bs[i], bs[j] = bs[j], bs[i] }

func (bs *Bins) Push(x interface{}) {
	*bs = append(*bs, x.(*Bin))
}

func (bs *Bins) Pop() interface{} {
	return bs.remove(len(*bs) - 1)
}

func (bs *Bins) remove(n int) *Bin {
	a := *bs
	if n < 0 || len(a) < n {
		return nil
	}
	x := a[n]
	*bs = append(a[:n], a[n+1:]...)
	return x
}

type Histogram struct {
	res *reservoir
}

func New(maxBins int) *Histogram {
	return &Histogram{res: newReservoir(maxBins)}
}

func (h *Histogram) Insert(f float64) {
	h.res.insert(&Bin{1, f})
	h.res.compress()
}

func (h *Histogram) Bins() Bins {
	return h.res.bins
}

type reservoir struct {
	n       int
	maxBins int
	bins    Bins
}

func newReservoir(maxBins int) *reservoir {
	return &reservoir{maxBins: maxBins}
}

func (r *reservoir) insert(bin *Bin) {
	r.n += bin.Count
	i := sort.Search(len(r.bins), func(i int) bool {
		return r.bins[i].Mean() >= bin.Mean()
	})
	if i < 0 || i == r.bins.Len() {
		// TODO(blake): Maybe use an .insert(i, bin) instead of
		// performing the extra work of a heap.Push.
		heap.Push(&r.bins, bin)
		return
	}
	r.bins[i].Update(bin)
}

func (r *reservoir) compress() {
	for r.bins.Len() > r.maxBins {
		minGapIndex := -1
		minGap := math.MaxFloat64
		for i := 0; i < r.bins.Len()-1; i++ {
			gap := gapWeight(r.bins[i], r.bins[i+1])
			if minGap > gap {
				minGap = gap
				minGapIndex = i
			}
		}
		prev := r.bins[minGapIndex]
		next := r.bins.remove(minGapIndex + 1)
		prev.Update(next)
	}
}

func gapWeight(prev, next *Bin) float64 {
	return next.Mean() - prev.Mean()
}
<|endoftext|>


<commit_before>package kafkafs

import (
	"fmt"
	"log"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"
)

// Implements a FUSE filesystem backed by a Kafka installation.
//
// This version is read only, so it cannot post to topics, only read
// from them.
type KafkaRoFs struct {
	pathfs.FileSystem

	KafkaClient KafkaClient
	userFiles   map[string]bool
	userFilesM  sync.RWMutex
}

type parsedPath struct {
	IsValid   bool
	IsRoot    bool
	Topic     string
	Partition int32
	Offset    int64
}

func (fs *KafkaRoFs) parseAndValidate(name string) (parsedPath, error) {
	parsed := parsedPath{IsValid: true, IsRoot: false, Topic: "", Partition: -1,
		Offset: -1}
	slashed := filepath.ToSlash(name)
	re := regexp.MustCompile("/{2,}")
	normal := re.ReplaceAllString(slashed, "/")
	if normal == "" {
		parsed.IsRoot = true
		return parsed, nil
	}

	splits := strings.Split(normal, "/")

	if len(splits) > 3 {
		parsed.IsValid = false
		return parsed, nil
	}

	if len(splits) >= 1 {
		maybeTopic := splits[0]
		isTop, err := fs.isTopic(maybeTopic)
		if err != nil {
			return parsed, err
		}
		if !isTop {
			parsed.IsValid = false
			return parsed, nil
		}
		parsed.Topic = maybeTopic
	}

	if len(splits) >= 2 {
		maybePartition := splits[1]
		isPart, err := fs.isPartition(parsed.Topic, maybePartition)
		if err != nil {
			return parsed, err
		}
		if !isPart {
			parsed.IsValid = false
			return parsed, nil
		}
		// this should always succeed if isPartition returned true.
		partition64, _ := strconv.ParseInt(maybePartition, 10, 32)
		parsed.Partition = int32(partition64)
	}

	if len(splits) == 3 {
		maybeOffset := splits[2]
		offset, err := strconv.ParseInt(maybeOffset, 10, 64)
		if err != nil {
			parsed.IsValid = false
			return parsed, nil
		}
		parsed.Offset = offset
	}

	return parsed, nil
}

func (fs *KafkaRoFs) isTopic(maybeTopic string) (bool, error) {
	topics, err := fs.KafkaClient.GetTopics()
	if err != nil {
		return false, err
	}

	for _, topic := range topics {
		if topic == maybeTopic {
			return true, nil
		}
	}

	return false, nil
}

func (fs *KafkaRoFs) isPartition(topic string, maybePartition string) (bool, error) {
	maybePartition64, err := strconv.ParseInt(maybePartition, 10, 32)
	if err != nil {
		return false, nil
	}
	maybePartition32 := int32(maybePartition64)
	partitions, err := fs.KafkaClient.GetPartitions(topic)
	if err != nil {
		return false, err
	}

	for _, partition := range partitions {
		if partition == maybePartition32 {
			return true, nil
		}
	}

	return false, nil
}

func (fs *KafkaRoFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr,
	fuse.Status) {
	log.Printf("GetAttr name: %s", name)

	parsed, err := fs.parseAndValidate(name)

	if err != nil {
		return nil, fuse.EIO
	}

	if !parsed.IsValid {
		return nil, fuse.ENOENT
	}

	switch {
	// root or a topic
	case parsed.IsRoot, parsed.Offset == -1 && parsed.Partition == -1:
		return &fuse.Attr{Mode: fuse.S_IFDIR | 0500}, fuse.OK
	// partition
	case parsed.Offset == -1:
		return &fuse.Attr{Mode: fuse.S_IFDIR | 0700}, fuse.OK
	// offset / msg
	case true:
		msgBytes, err := fs.KafkaClient.GetMessage(parsed.Topic, parsed.Partition,
			parsed.Offset)
		if err != nil {
			return nil, fuse.EIO
		}
		if msgBytes == nil {
			return nil, fuse.ENOENT
		}
		fs.addUserFile(parsed.Topic, parsed.Partition, parsed.Offset)
		return &fuse.Attr{Mode: fuse.S_IFREG | 0400,
			Size: uint64(len(msgBytes))}, fuse.OK
	}

	return nil, fuse.ENOENT
}

func (fs *KafkaRoFs) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry,
	fuse.Status) {
	log.Printf("OpenDir name: %s", name)

	parsed, err := fs.parseAndValidate(name)
	if err != nil {
		return nil, fuse.EIO
	}

	if !parsed.IsValid {
		return nil, fuse.ENOENT
	}

	switch {
	case parsed.IsRoot:
		return fs.openRoot(context)
	case parsed.Partition == -1:
		return fs.openTopic(parsed.Topic, context)
	case parsed.Offset == -1:
		return fs.openPartition(parsed.Topic, parsed.Partition, context)
	}

	return nil, fuse.ENOENT
}

func (fs *KafkaRoFs) openPartition(topic string, partition int32,
	context *fuse.Context) ([]fuse.DirEntry,
	fuse.Status) {
	earliest, next, err := fs.KafkaClient.GetBoundingOffsets(topic, partition)
	if err != nil {
		return nil, fuse.EIO
	}
	if next == int64(0) {
		// this means an empty partition
		return []fuse.DirEntry{}, fuse.OK
	}

	entries := []fuse.DirEntry{fuse.DirEntry{Name: strconv.FormatInt(earliest, 10),
		Mode: fuse.S_IFREG}}
	if earliest != next-1 {
		entries = append(entries, fuse.DirEntry{Name: strconv.FormatInt(next-1, 10),
			Mode: fuse.S_IFREG})
	}

	var paths []string
	fs.userFilesM.RLock()
	for path, _ := range fs.userFiles {
		paths = append(paths, path)
	}
	fs.userFilesM.RUnlock()

	for _, path := range paths {
		parsed, err := fs.parseAndValidate(path)
		if err != nil {
			log.Panicf("Error was non-nil, bad user path %s", err)
		}
		if parsed.Partition == partition && parsed.Offset != earliest &&
			parsed.Offset != next-1 {
			entries = append(entries, fuse.DirEntry{
				Name: strconv.FormatInt(parsed.Offset, 10), Mode: fuse.S_IFREG})
		}
	}

	return entries, fuse.OK
}

func (fs *KafkaRoFs) openRoot(context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	// root, show all the topics
	var topicDirs []fuse.DirEntry
	topics, err := fs.KafkaClient.GetTopics()

	if err != nil {
		return nil, fuse.EIO
	}

	for _, topic := range topics {
		topicDirs = append(topicDirs, fuse.DirEntry{Name: topic, Mode: fuse.S_IFDIR})
	}

	return topicDirs, fuse.OK
}

func (fs *KafkaRoFs) openTopic(name string, context *fuse.Context) ([]fuse.DirEntry,
	fuse.Status) {
	var partitionDirs []fuse.DirEntry
	partitions, err := fs.KafkaClient.GetPartitions(name)

	if err != nil {
		return nil, fuse.EIO
	}

	for _, partition := range partitions {
		partitionDirs = append(partitionDirs,
			fuse.DirEntry{Name: strconv.FormatInt(int64(partition), 10),
				Mode: fuse.S_IFDIR})
	}

	return partitionDirs, fuse.OK
}

func (fs *KafkaRoFs) Open(name string, flags uint32,
	context *fuse.Context) (nodefs.File, fuse.Status) {
	parsed, err := fs.parseAndValidate(name)
	if err != nil {
		return nil, fuse.EIO
	}

	if !parsed.IsValid || parsed.Offset == -1 {
		return nil, fuse.ENOENT
	}

	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}

	msgBytes, err := fs.KafkaClient.GetMessage(parsed.Topic, parsed.Partition,
		parsed.Offset)
	if err != nil {
		return nil, fuse.EIO
	}

	return nodefs.NewDataFile(msgBytes), fuse.OK
}

func (fs *KafkaRoFs) Mknod(name string, mode uint32, dev uint32,
	context *fuse.Context) fuse.Status {

	parsed, err := fs.parseAndValidate(name)
	if err != nil {
		return fuse.EIO
	}

	if !parsed.IsValid || parsed.Offset == -1 {
		return fuse.ENOENT
	}

	// we don't want to block waiting on an offset that is in the past
	// and no longer available from kafka

	nE := func() (bool, error) {
		return fs.offsetNotExpired(parsed.Topic, parsed.Partition, parsed.Offset)
	}

	for notExpired, err := nE(); notExpired; notExpired, err = nE() {
		if err != nil {
			return fuse.EIO
		}

		isFuture, err := fs.offsetIsFuture(parsed.Topic, parsed.Partition,
			parsed.Offset)
		if err != nil {
			return fuse.EIO
		}

		if isFuture {
			time.Sleep(1 * time.Second)
			continue
		}

		msgBytes, err := fs.KafkaClient.GetMessage(parsed.Topic, parsed.Partition,
			parsed.Offset)
		if err != nil {
			return fuse.EIO
		}

		if msgBytes == nil {
			// this shouldn't happen unless the messages are expiring
			// very fast, but who knows
			return fuse.ENOENT
		}

		err = fs.addUserFile(parsed.Topic, parsed.Partition, parsed.Offset)
		if err != nil {
			return fuse.EIO
		}

		return fuse.OK
	}

	return fuse.ENOENT
}

func (fs *KafkaRoFs) addUserFile(topic string, partition int32, offset int64) error {
	path := fmt.Sprintf("%s/%d/%d", topic, partition, offset)
	fs.userFilesM.Lock()
	fs.userFiles[path] = true
	fs.userFilesM.Unlock()
	return nil
}

func (fs *KafkaRoFs) offsetNotExpired(topic string, partition int32,
	offset int64) (bool, error) {
	earliest, _, err := fs.KafkaClient.GetBoundingOffsets(topic, partition)
	if err != nil {
		return false, err
	}
	return offset >= earliest, nil
}

func (fs *KafkaRoFs) offsetIsFuture(topic string, partition int32,
	offset int64) (bool, error) {
	_, next, err := fs.KafkaClient.GetBoundingOffsets(topic, partition)
	if err != nil {
		return false, err
	}
	return offset >= next, nil
}

// just pretend we set the times to keep touch happy
func (fs *KafkaRoFs) Utimens(name string, Atime *time.Time, Mtime *time.Time,
	context *fuse.Context) fuse.Status {
	return fuse.OK
}

func NewKafkaRoFs(kClient KafkaClient) *KafkaRoFs {
	return &KafkaRoFs{FileSystem: pathfs.NewDefaultFileSystem(), KafkaClient: kClient,
		userFiles: make(map[string]bool)}
}

func (fs *KafkaRoFs) Unlink(name string, context *fuse.Context) fuse.Status {
	_, ok := fs.userFiles[name]
	if !ok {
		return fuse.EPERM
	}
	delete(fs.userFiles, name)
	return fuse.OK
}
<commit_msg>Get rid of mknod for offset fetching, just try to read a file.<commit_after>package kafkafs

import (
	"fmt"
	"log"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"
)

// Implements a FUSE filesystem backed by a Kafka installation.
//
// This version is read only, so it cannot post to topics, only read
// from them.
type KafkaRoFs struct {
	pathfs.FileSystem

	KafkaClient KafkaClient
	userFiles   map[string]bool
	userFilesM  sync.RWMutex
}

type parsedPath struct {
	IsValid   bool
	IsRoot    bool
	Topic     string
	Partition int32
	Offset    int64
}

func (fs *KafkaRoFs) parseAndValidate(name string) (parsedPath, error) {
	parsed := parsedPath{IsValid: true, IsRoot: false, Topic: "", Partition: -1,
		Offset: -1}
	slashed := filepath.ToSlash(name)
	re := regexp.MustCompile("/{2,}")
	normal := re.ReplaceAllString(slashed, "/")
	if normal == "" {
		parsed.IsRoot = true
		return parsed, nil
	}

	splits := strings.Split(normal, "/")

	if len(splits) > 3 {
		parsed.IsValid = false
		return parsed, nil
	}

	if len(splits) >= 1 {
		maybeTopic := splits[0]
		isTop, err := fs.isTopic(maybeTopic)
		if err != nil {
			return parsed, err
		}
		if !isTop {
			parsed.IsValid = false
			return parsed, nil
		}
		parsed.Topic = maybeTopic
	}

	if len(splits) >= 2 {
		maybePartition := splits[1]
		isPart, err := fs.isPartition(parsed.Topic, maybePartition)
		if err != nil {
			return parsed, err
		}
		if !isPart {
			parsed.IsValid = false
			return parsed, nil
		}
		// this should always succeed if isPartition returned true.
		partition64, _ := strconv.ParseInt(maybePartition, 10, 32)
		parsed.Partition = int32(partition64)
	}

	if len(splits) == 3 {
		maybeOffset := splits[2]
		offset, err := strconv.ParseInt(maybeOffset, 10, 64)
		if err != nil {
			parsed.IsValid = false
			return parsed, nil
		}
		parsed.Offset = offset
	}

	return parsed, nil
}

func (fs *KafkaRoFs) isTopic(maybeTopic string) (bool, error) {
	topics, err := fs.KafkaClient.GetTopics()
	if err != nil {
		return false, err
	}

	for _, topic := range topics {
		if topic == maybeTopic {
			return true, nil
		}
	}

	return false, nil
}

func (fs *KafkaRoFs) isPartition(topic string, maybePartition string) (bool, error) {
	maybePartition64, err := strconv.ParseInt(maybePartition, 10, 32)
	if err != nil {
		return false, nil
	}
	maybePartition32 := int32(maybePartition64)
	partitions, err := fs.KafkaClient.GetPartitions(topic)
	if err != nil {
		return false, err
	}

	for _, partition := range partitions {
		if partition == maybePartition32 {
			return true, nil
		}
	}

	return false, nil
}

func (fs *KafkaRoFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr,
	fuse.Status) {
	log.Printf("GetAttr name: %s", name)

	parsed, err := fs.parseAndValidate(name)

	if err != nil {
		return nil, fuse.EIO
	}

	if !parsed.IsValid {
		return nil, fuse.ENOENT
	}

	switch {
	// root or a topic
	case parsed.IsRoot, parsed.Offset == -1 && parsed.Partition == -1:
		return &fuse.Attr{Mode: fuse.S_IFDIR | 0500}, fuse.OK
	// partition
	case parsed.Offset == -1:
		return &fuse.Attr{Mode: fuse.S_IFDIR | 0700}, fuse.OK
	// offset / msg
	case true:
		msgBytes, err := fs.KafkaClient.GetMessage(parsed.Topic, parsed.Partition,
			parsed.Offset)
		if err != nil {
			return nil, fuse.EIO
		}
		if msgBytes == nil {
			return nil, fuse.ENOENT
		}
		fs.addUserFile(parsed.Topic, parsed.Partition, parsed.Offset)
		return &fuse.Attr{Mode: fuse.S_IFREG | 0400,
			Size: uint64(len(msgBytes))}, fuse.OK
	}

	return nil, fuse.ENOENT
}

func (fs *KafkaRoFs) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry,
	fuse.Status) {
	log.Printf("OpenDir name: %s", name)

	parsed, err := fs.parseAndValidate(name)
	if err != nil {
		return nil, fuse.EIO
	}

	if !parsed.IsValid {
		return nil, fuse.ENOENT
	}

	switch {
	case parsed.IsRoot:
		return fs.openRoot(context)
	case parsed.Partition == -1:
		return fs.openTopic(parsed.Topic, context)
	case parsed.Offset == -1:
		return fs.openPartition(parsed.Topic, parsed.Partition, context)
	}

	return nil, fuse.ENOENT
}

func (fs *KafkaRoFs) openPartition(topic string, partition int32,
	context *fuse.Context) ([]fuse.DirEntry,
	fuse.Status) {
	earliest, next, err := fs.KafkaClient.GetBoundingOffsets(topic, partition)
	if err != nil {
		return nil, fuse.EIO
	}
	if next == int64(0) {
		// this means an empty partition
		return []fuse.DirEntry{}, fuse.OK
	}

	entries := []fuse.DirEntry{fuse.DirEntry{Name: strconv.FormatInt(earliest, 10),
		Mode: fuse.S_IFREG}}
	if earliest != next-1 {
		entries = append(entries, fuse.DirEntry{Name: strconv.FormatInt(next-1, 10),
			Mode: fuse.S_IFREG})
	}

	var paths []string
	fs.userFilesM.RLock()
	for path, _ := range fs.userFiles {
		paths = append(paths, path)
	}
	fs.userFilesM.RUnlock()

	for _, path := range paths {
		parsed, err := fs.parseAndValidate(path)
		if err != nil {
			log.Panicf("Error was non-nil, bad user path %s", err)
		}
		if parsed.Partition == partition && parsed.Offset != earliest &&
			parsed.Offset != next-1 {
			entries = append(entries, fuse.DirEntry{
				Name: strconv.FormatInt(parsed.Offset, 10), Mode: fuse.S_IFREG})
		}
	}

	return entries, fuse.OK
}

func (fs *KafkaRoFs) openRoot(context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	// root, show all the topics
	var topicDirs []fuse.DirEntry
	topics, err := fs.KafkaClient.GetTopics()

	if err != nil {
		return nil, fuse.EIO
	}

	for _, topic := range topics {
		topicDirs = append(topicDirs, fuse.DirEntry{Name: topic, Mode: fuse.S_IFDIR})
	}

	return topicDirs, fuse.OK
}

func (fs *KafkaRoFs) openTopic(name string, context *fuse.Context) ([]fuse.DirEntry,
	fuse.Status) {
	var partitionDirs []fuse.DirEntry
	partitions, err := fs.KafkaClient.GetPartitions(name)

	if err != nil {
		return nil, fuse.EIO
	}

	for _, partition := range partitions {
		partitionDirs = append(partitionDirs,
			fuse.DirEntry{Name: strconv.FormatInt(int64(partition), 10),
				Mode: fuse.S_IFDIR})
	}

	return partitionDirs, fuse.OK
}

func (fs *KafkaRoFs) Open(name string, flags uint32,
	context *fuse.Context) (nodefs.File, fuse.Status) {
	parsed, err := fs.parseAndValidate(name)
	if err != nil {
		return nil, fuse.EIO
	}

	if !parsed.IsValid || parsed.Offset == -1 {
		return nil, fuse.ENOENT
	}

	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}

	// we don't want to block waiting on an offset that is in the past
	// and no longer available from kafka
	nE := func() (bool, error) {
		return fs.offsetNotExpired(parsed.Topic, parsed.Partition, parsed.Offset)
	}

	for notExpired, err := nE(); notExpired; notExpired, err = nE() {
		if err != nil {
			return nil, fuse.EIO
		}

		isFuture, err := fs.offsetIsFuture(parsed.Topic, parsed.Partition,
			parsed.Offset)
		if err != nil {
			return nil, fuse.EIO
		}

		if isFuture {
			time.Sleep(1 * time.Millisecond)
			continue
		}

		msgBytes, err := fs.KafkaClient.GetMessage(parsed.Topic, parsed.Partition,
			parsed.Offset)
		if err != nil {
			return nil, fuse.EIO
		}

		if msgBytes == nil {
			// this shouldn't happen unless the messages are expiring
			// very fast, but who knows
			return nil, fuse.ENOENT
		}

		err = fs.addUserFile(parsed.Topic, parsed.Partition, parsed.Offset)
		if err != nil {
			return nil, fuse.EIO
		}

		return nodefs.NewDataFile(msgBytes), fuse.OK
	}

	return nil, fuse.ENOENT
}

func (fs *KafkaRoFs) addUserFile(topic string, partition int32, offset int64) error {
	path := fmt.Sprintf("%s/%d/%d", topic, partition, offset)
	fs.userFilesM.Lock()
	fs.userFiles[path] = true
	fs.userFilesM.Unlock()
	return nil
}

func (fs *KafkaRoFs) offsetNotExpired(topic string, partition int32,
	offset int64) (bool, error) {
	earliest, _, err := fs.KafkaClient.GetBoundingOffsets(topic, partition)
	if err != nil {
		return false, err
	}
	return offset >= earliest, nil
}

func (fs *KafkaRoFs) offsetIsFuture(topic string, partition int32,
	offset int64) (bool, error) {
	_, next, err := fs.KafkaClient.GetBoundingOffsets(topic, partition)
	if err != nil {
		return false, err
	}
	return offset >= next, nil
}

// just pretend we set the times to keep touch happy
func (fs *KafkaRoFs) Utimens(name string, Atime *time.Time, Mtime *time.Time,
	context *fuse.Context) fuse.Status {
	return fuse.OK
}

func NewKafkaRoFs(kClient KafkaClient) *KafkaRoFs {
	return &KafkaRoFs{FileSystem: pathfs.NewDefaultFileSystem(), KafkaClient: kClient,
		userFiles: make(map[string]bool)}
}

func (fs *KafkaRoFs) Unlink(name string, context *fuse.Context) fuse.Status {
	_, ok := fs.userFiles[name]
	if !ok {
		return fuse.EPERM
	}
	delete(fs.userFiles, name)
	return fuse.OK
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
)

var eventTypes = []string{"logging", "operation", "lifecycle"}

var eventsCmd = APIEndpoint{
	Path: "events",

	Get: APIEndpointAction{Handler: eventsGet, AccessHandler: allowAuthenticated},
}

type eventsServe struct {
	req *http.Request
	d   *Daemon
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.d, r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error {
	project := projectParam(r)
	types := strings.Split(r.FormValue("type"), ",")
	if len(types) == 1 && types[0] == "" {
		types = eventTypes
	}

	// Validate event types.
	for _, entry := range types {
		if !shared.StringInSlice(entry, eventTypes) {
			response.BadRequest(fmt.Errorf("'%s' isn't a supported event type", entry)).Render(w)
			return nil
		}
	}

	if shared.StringInSlice("logging", types) && !d.userIsAdmin(r) {
		response.Forbidden(nil).Render(w)
		return nil
	}

	// Upgrade the connection to websocket
	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}
	defer c.Close() // This ensures the go routine below is ended when this function ends.

	// Get the current local serverName and store it for the events
	// We do that now to avoid issues with changes to the name and to limit
	// the number of DB access to just one per connection
	var serverName string
	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
		serverName, err = tx.GetLocalNodeName()
		return err
	})
	if err != nil {
		return err
	}

	// If this request is an internal one initiated by another node wanting
	// to watch the events on this node, set the listener to broadcast only
	// local events.
	listener, err := d.events.AddListener(project, c, types, serverName, isClusterNotification(r))
	if err != nil {
		return err
	}

	logger.Debugf("New event listener: %s", listener.ID())

	// Create a cancellable context from the request context. Once the request has been upgraded
	// to a websocket the request's context doesn't appear to be cancelled when the client
	// disconnects (even though its documented as such). But we wrap the request's context here
	// anyway just in case its fixed in the future.
	ctx, cancel := context.WithCancel(r.Context())

	// Instead of relying on the request's context to be cancelled when the client connection
	// is closed (see above), we instead enter into a repeat read loop of the connection in
	// order to detect when the client connection is closed. This should be fine as for the
	// events route there is no expectation to read any useful data from the client.
	go func() {
		for {
			_, _, err := c.NextReader()
			if err != nil {
				// Client read error (likely premature close), so cancel context.
				cancel()
				return
			}
		}
	}()

	listener.Wait(ctx)
	logger.Debugf("Event listener finished: %s", listener.ID())

	return nil
}

func eventsGet(d *Daemon, r *http.Request) response.Response {
	return &eventsServe{req: r, d: d}
}
<commit_msg>lxd/events: Handle default permissiosn in projects<commit_after>package main

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
)

var eventTypes = []string{"logging", "operation", "lifecycle"}
var privilegedEventTypes = []string{"logging"}

var eventsCmd = APIEndpoint{
	Path: "events",

	Get: APIEndpointAction{Handler: eventsGet, AccessHandler: allowAuthenticated},
}

type eventsServe struct {
	req *http.Request
	d   *Daemon
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.d, r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error {
	project := projectParam(r)
	types := strings.Split(r.FormValue("type"), ",")
	if len(types) == 1 && types[0] == "" {
		types = []string{}
		for _, entry := range eventTypes {
			if !d.userIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) {
				continue
			}

			types = append(types, entry)
		}
	}

	// Validate event types.
	for _, entry := range types {
		if !shared.StringInSlice(entry, eventTypes) {
			response.BadRequest(fmt.Errorf("'%s' isn't a supported event type", entry)).Render(w)
			return nil
		}
	}

	if shared.StringInSlice("logging", types) && !d.userIsAdmin(r) {
		response.Forbidden(nil).Render(w)
		return nil
	}

	// Upgrade the connection to websocket
	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}
	defer c.Close() // This ensures the go routine below is ended when this function ends.

	// Get the current local serverName and store it for the events
	// We do that now to avoid issues with changes to the name and to limit
	// the number of DB access to just one per connection
	var serverName string
	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
		serverName, err = tx.GetLocalNodeName()
		return err
	})
	if err != nil {
		return err
	}

	// If this request is an internal one initiated by another node wanting
	// to watch the events on this node, set the listener to broadcast only
	// local events.
	listener, err := d.events.AddListener(project, c, types, serverName, isClusterNotification(r))
	if err != nil {
		return err
	}

	logger.Debugf("New event listener: %s", listener.ID())

	// Create a cancellable context from the request context. Once the request has been upgraded
	// to a websocket the request's context doesn't appear to be cancelled when the client
	// disconnects (even though its documented as such). But we wrap the request's context here
	// anyway just in case its fixed in the future.
	ctx, cancel := context.WithCancel(r.Context())

	// Instead of relying on the request's context to be cancelled when the client connection
	// is closed (see above), we instead enter into a repeat read loop of the connection in
	// order to detect when the client connection is closed. This should be fine as for the
	// events route there is no expectation to read any useful data from the client.
	go func() {
		for {
			_, _, err := c.NextReader()
			if err != nil {
				// Client read error (likely premature close), so cancel context.
				cancel()
				return
			}
		}
	}()

	listener.Wait(ctx)
	logger.Debugf("Event listener finished: %s", listener.ID())

	return nil
}

func eventsGet(d *Daemon, r *http.Request) response.Response {
	return &eventsServe{req: r, d: d}
}
<|endoftext|>


<commit_before>//
//   date  : 2016-05-13
//   author: xjdrew
//

package k1

import (
	"fmt"
	"io"
	"net"

	"github.com/xjdrew/kone/tcpip"
)

type halfCloseConn interface {
	net.Conn
	CloseRead() error
	CloseWrite() error
}

type TCPRelay struct {
	one       *One
	nat       *Nat
	relayIP   net.IP
	relayPort uint16
}

func copy(src net.Conn, dst net.Conn, ch chan<- int64) {
	written, _ := io.Copy(dst, src)
	ch <- written
}

func copyAndClose(src halfCloseConn, dst halfCloseConn, ch chan<- int64) {
	written, _ := io.Copy(dst, src)

	dst.CloseWrite()
	src.CloseRead()
	ch <- written
}

func (r *TCPRelay) realRemoteHost(conn net.Conn, connData *ConnData) (addr string, proxy string) {
	remoteAddr := conn.RemoteAddr().(*net.TCPAddr)
	remotePort := uint16(remoteAddr.Port)

	session := r.nat.getSession(remotePort)
	if session == nil {
		logger.Errorf("[tcp] %s > %s no session", conn.LocalAddr(), remoteAddr)
		return
	}

	one := r.one

	var host string
	if record := one.dnsTable.GetByIP(session.dstIP); record != nil {
		host = record.Hostname
		proxy = record.Proxy
	} else if one.dnsTable.Contains(session.dstIP) {
		logger.Debugf("[tcp] %s:%d > %s:%d dns expired", session.srcIP, session.srcPort, session.dstIP, session.dstPort)
		return
	} else {
		host = session.dstIP.String()
	}

	connData.Src = session.srcIP.String()
	connData.Dst = host
	connData.Proxy = proxy

	addr = fmt.Sprintf("%s:%d", host, session.dstPort)
	logger.Debugf("[tcp] %s:%d > %s proxy %q", session.srcIP, session.srcPort, addr, proxy)
	return
}

func (r *TCPRelay) handleConn(conn net.Conn) {
	var connData ConnData
	remoteAddr, proxy := r.realRemoteHost(conn, &connData)
	if remoteAddr == "" {
		conn.Close()
		return
	}

	proxies := r.one.proxies
	tunnel, err := proxies.Dial(proxy, remoteAddr)
	if err != nil {
		conn.Close()
		logger.Errorf("[tcp] dial %s by proxy %q failed: %s", remoteAddr, proxy, err)
		return
	}

	uploadChan := make(chan int64)
	downloadChan := make(chan int64)

	connHCC, connOK := conn.(halfCloseConn)
	tunnelHCC, tunnelOK := tunnel.(halfCloseConn)
	if connOK && tunnelOK {
		go copyAndClose(connHCC, tunnelHCC, uploadChan)
		go copyAndClose(tunnelHCC, connHCC, downloadChan)
	} else {
		go copy(conn, tunnel, uploadChan)
		go copy(tunnel, conn, downloadChan)
		defer conn.Close()
		defer tunnel.Close()
	}
	connData.Upload = <-uploadChan
	connData.Download = <-downloadChan

	if r.one.manager != nil {
		r.one.manager.dataCh <- connData
	}
}

func (r *TCPRelay) Serve() error {
	addr := &net.TCPAddr{IP: r.relayIP, Port: int(r.relayPort)}
	ln, err := net.ListenTCP("tcp4", addr)
	if err != nil {
		return err
	}

	logger.Infof("[tcp] listen on %v", addr)

	for {
		conn, err := ln.AcceptTCP()
		if err != nil {
			return err
		}
		go r.handleConn(conn)
	}
}

// redirect tcp packet to relay
func (r *TCPRelay) Filter(wr io.Writer, ipPacket tcpip.IPv4Packet) {
	tcpPacket := tcpip.TCPPacket(ipPacket.Payload())

	srcIP := ipPacket.SourceIP()
	dstIP := ipPacket.DestinationIP()
	srcPort := tcpPacket.SourcePort()
	dstPort := tcpPacket.DestinationPort()

	if r.relayIP.Equal(srcIP) && srcPort == r.relayPort {
		// from relay
		session := r.nat.getSession(dstPort)
		if session == nil {
			logger.Debugf("[tcp] %s:%d > %s:%d: no session", srcIP, srcPort, dstIP, dstPort)
			return
		}

		ipPacket.SetSourceIP(session.dstIP)
		ipPacket.SetDestinationIP(session.srcIP)
		tcpPacket.SetSourcePort(session.dstPort)
		tcpPacket.SetDestinationPort(session.srcPort)
	} else {
		// redirect to relay
		isNew, port := r.nat.allocSession(srcIP, dstIP, srcPort, dstPort)

		ipPacket.SetSourceIP(dstIP)
		tcpPacket.SetSourcePort(port)
		ipPacket.SetDestinationIP(r.relayIP)
		tcpPacket.SetDestinationPort(r.relayPort)

		if isNew {
			logger.Debugf("[tcp] %s:%d > %s:%d: shape to %s:%d > %s:%d",
				srcIP, srcPort, dstIP, dstPort, dstIP, port, r.relayIP, r.relayPort)
		}
	}

	// write back packet
	tcpPacket.ResetChecksum(ipPacket.PseudoSum())
	ipPacket.ResetChecksum()
	wr.Write(ipPacket)
}

func NewTCPRelay(one *One, cfg NatConfig) *TCPRelay {
	relay := new(TCPRelay)
	relay.one = one
	relay.nat = NewNat(cfg.NatPortStart, cfg.NatPortEnd)
	relay.relayIP = one.ip
	relay.relayPort = cfg.ListenPort
	return relay
}
<commit_msg>bugfix #15<commit_after>//
//   date  : 2016-05-13
//   author: xjdrew
//

package k1

import (
	"fmt"
	"io"
	"net"

	"github.com/xjdrew/kone/tcpip"
)

type halfCloseConn interface {
	net.Conn
	CloseRead() error
	CloseWrite() error
}

type TCPRelay struct {
	one       *One
	nat       *Nat
	relayIP   net.IP
	relayPort uint16
}

func copy(src net.Conn, dst net.Conn, ch chan<- int64) {
	written, _ := io.Copy(dst, src)
	ch <- written
}

func copyAndClose(src halfCloseConn, dst halfCloseConn, ch chan<- int64) {
	written, _ := io.Copy(dst, src)

	dst.CloseWrite()
	src.CloseRead()
	ch <- written
}

func (r *TCPRelay) realRemoteHost(conn net.Conn, connData *ConnData) (addr string, proxy string) {
	remoteAddr := conn.RemoteAddr().(*net.TCPAddr)
	remotePort := uint16(remoteAddr.Port)

	session := r.nat.getSession(remotePort)
	if session == nil {
		logger.Errorf("[tcp] %s > %s no session", conn.LocalAddr(), remoteAddr)
		return
	}

	one := r.one

	var host string
	if record := one.dnsTable.GetByIP(session.dstIP); record != nil {
		host = record.Hostname
		proxy = record.Proxy
	} else if one.dnsTable.Contains(session.dstIP) {
		logger.Debugf("[tcp] %s:%d > %s:%d dns expired", session.srcIP, session.srcPort, session.dstIP, session.dstPort)
		return
	} else {
		host = session.dstIP.String()
	}

	connData.Src = session.srcIP.String()
	connData.Dst = host
	connData.Proxy = proxy

	addr = fmt.Sprintf("%s:%d", host, session.dstPort)
	logger.Debugf("[tcp] %s:%d > %s proxy %q", session.srcIP, session.srcPort, addr, proxy)
	return
}

func (r *TCPRelay) handleConn(conn net.Conn) {
	var connData ConnData
	remoteAddr, proxy := r.realRemoteHost(conn, &connData)
	if remoteAddr == "" {
		conn.Close()
		return
	}

	proxies := r.one.proxies
	tunnel, err := proxies.Dial(proxy, remoteAddr)
	if err != nil {
		conn.Close()
		logger.Errorf("[tcp] dial %s by proxy %q failed: %s", remoteAddr, proxy, err)
		return
	}

	uploadChan := make(chan int64)
	downloadChan := make(chan int64)

	connHCC, connOK := conn.(halfCloseConn)
	tunnelHCC, tunnelOK := tunnel.(halfCloseConn)
	if connOK && tunnelOK {
		go copyAndClose(connHCC, tunnelHCC, uploadChan)
		go copyAndClose(tunnelHCC, connHCC, downloadChan)
	} else {
		go copy(conn, tunnel, uploadChan)
		go copy(tunnel, conn, downloadChan)
		defer conn.Close()
		defer tunnel.Close()
	}
	connData.Upload = <-uploadChan
	connData.Download = <-downloadChan

	if r.one.manager != nil {
		r.one.manager.dataCh <- connData
	}
}

func (r *TCPRelay) Serve() error {
	addr := &net.TCPAddr{IP: r.relayIP, Port: int(r.relayPort)}
	ln, err := net.ListenTCP("tcp4", addr)
	if err != nil {
		return err
	}

	logger.Infof("[tcp] listen on %v", addr)

	for {
		conn, err := ln.AcceptTCP()
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
				logger.Errorf("acceept failed temporary: %s", netErr.Error())
				continue
			} else {
				return err
			}
		}
		go r.handleConn(conn)
	}
}

// redirect tcp packet to relay
func (r *TCPRelay) Filter(wr io.Writer, ipPacket tcpip.IPv4Packet) {
	tcpPacket := tcpip.TCPPacket(ipPacket.Payload())

	srcIP := ipPacket.SourceIP()
	dstIP := ipPacket.DestinationIP()
	srcPort := tcpPacket.SourcePort()
	dstPort := tcpPacket.DestinationPort()

	if r.relayIP.Equal(srcIP) && srcPort == r.relayPort {
		// from relay
		session := r.nat.getSession(dstPort)
		if session == nil {
			logger.Debugf("[tcp] %s:%d > %s:%d: no session", srcIP, srcPort, dstIP, dstPort)
			return
		}

		ipPacket.SetSourceIP(session.dstIP)
		ipPacket.SetDestinationIP(session.srcIP)
		tcpPacket.SetSourcePort(session.dstPort)
		tcpPacket.SetDestinationPort(session.srcPort)
	} else {
		// redirect to relay
		isNew, port := r.nat.allocSession(srcIP, dstIP, srcPort, dstPort)

		ipPacket.SetSourceIP(dstIP)
		tcpPacket.SetSourcePort(port)
		ipPacket.SetDestinationIP(r.relayIP)
		tcpPacket.SetDestinationPort(r.relayPort)

		if isNew {
			logger.Debugf("[tcp] %s:%d > %s:%d: shape to %s:%d > %s:%d",
				srcIP, srcPort, dstIP, dstPort, dstIP, port, r.relayIP, r.relayPort)
		}
	}

	// write back packet
	tcpPacket.ResetChecksum(ipPacket.PseudoSum())
	ipPacket.ResetChecksum()
	wr.Write(ipPacket)
}

func NewTCPRelay(one *One, cfg NatConfig) *TCPRelay {
	relay := new(TCPRelay)
	relay.one = one
	relay.nat = NewNat(cfg.NatPortStart, cfg.NatPortEnd)
	relay.relayIP = one.ip
	relay.relayPort = cfg.ListenPort
	return relay
}
<|endoftext|>