<commit_before>//
// Generate an RSS feed from a PostgreSQL database containing tweets.
//
// The tweet database is the one populated by my twitter-tcl twitter_poll
// program.
//
package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/horgh/config"
	"github.com/horgh/gorse/gorselib"
	_ "github.com/lib/pq"
)

// FeedURI is the URI set on the RSS feed's channel element's link element.
// It need not be a real URI but should be unique.
var FeedURI = "https://leviathan.summercat.com/tweets/"

// Tweet describe a tweet pulled from the database.
type Tweet struct {
	Nick    string
	Text    string
	Time    time.Time
	TweetID int64
}

// MyConfig holds configuration values.
type MyConfig struct {
	DBUser string
	DBPass string
	DBName string
	DBHost string
	// the number of recent tweets to put in the xml.
	NumTweets uint64
}

// connectToDB opens a new connection to the database.
func connectToDB(name string, user string, pass string, host string) (*sql.DB,
	error) {
	dsn := fmt.Sprintf("user=%s password=%s dbname=%s host=%s", user, pass, name,
		host)
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		log.Printf("Failed to connect to the database: %s", err)
		return nil, err
	}
	return db, nil
}

// getTweets retrieves tweets from a database.
func getTweets(config *MyConfig) ([]Tweet, error) {
	db, err := connectToDB(config.DBName, config.DBUser, config.DBPass,
		config.DBHost)
	if err != nil {
		return nil, err
	}

	// get most recent tweets.
	sql := `
SELECT nick, text, time, tweet_id
FROM tweet
ORDER BY time DESC
LIMIT $1
`
	rows, err := db.Query(sql, config.NumTweets)
	if err != nil {
		log.Printf("Query failure: %s", err)
		return nil, err
	}

	var tweets []Tweet
	for rows.Next() {
		tweet := Tweet{}
		err = rows.Scan(&tweet.Nick, &tweet.Text, &tweet.Time, &tweet.TweetID)
		if err != nil {
			log.Printf("Failed to scan row: %s", err)
			// TODO: is there anything to clean up?
			return nil, err
		}
		tweets = append(tweets, tweet)
	}

	// I'm adding a close because I see 'unexpected EOF on client connection'
	// in postgresql logs from this. with a close it goes away!
	err = db.Close()
	if err != nil {
		log.Printf("Failed to close database connection: %s", err)
		return nil, err
	}
	return tweets, nil
}

// create a URL to the status.
// apparently this URL is not in the tweet status payload.
// form:
// https://twitter.com/<screenname>/status/<tweetid>
func createStatusURL(screenName string, tweetID int64) string {
	return fmt.Sprintf("https://twitter.com/%s/status/%d",
		screenName, tweetID)
}

// main is the program entry point.
func main() {
	log.SetFlags(log.Ltime | log.Llongfile)

	// command line arguments.
	outputFile := flag.String("output-file", "", "Output XML file to write.")
	configFile := flag.String("config-file", "", "Config file")
	flag.Parse()
	if len(*outputFile) == 0 || len(*configFile) == 0 {
		fmt.Println("You must provide a config file.")
		flag.PrintDefaults()
		os.Exit(1)
	}

	var settings MyConfig
	err := config.GetConfig(*configFile, &settings)
	if err != nil {
		log.Fatalf("Failed to retrieve config: %s", err)
	}
	// TODO: We could run validation on each config item... but then again,
	//   we can just try to connect to the database!

	// reduce some library logging.
	gorselib.SetQuiet(true)

	// retrieve recent tweets.
	tweets, err := getTweets(&settings)
	if err != nil {
		log.Fatalf("Failed to retrieve tweets: %s", err)
	}

	// set up the feed's information.
	rss := gorselib.RSSFeed{}
	rss.Name = "Twitreader"
	rss.URI = FeedURI
	rss.Description = "Twitreader tweets"
	rss.LastUpdateTime = time.Now()

	// build rss items.
	for _, tweet := range tweets {
		item := gorselib.RSSItem{
			Title:           fmt.Sprintf("%s", tweet.Nick),
			URI:             createStatusURL(tweet.Nick, tweet.TweetID),
			Description:     tweet.Text,
			PublicationDate: tweet.Time,
		}
		rss.Items = append(rss.Items, item)
	}

	err = gorselib.WriteFeedXML(&rss, *outputFile)
	if err != nil {
		log.Fatalf("Failed to write XML: %s", err)
	}
}
<commit_msg>Clean up comments a bit.<commit_after>//
// Generate an RSS feed from a PostgreSQL database containing tweets.
//
// The tweet database is the one populated by my twitter-tcl twitter_poll
// program.
//
package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/horgh/config"
	"github.com/horgh/gorse/gorselib"
	_ "github.com/lib/pq"
)

// FeedURI is the URI set on the RSS feed's channel element's link element. It
// need not be a real URI but should be unique.
var FeedURI = "https://leviathan.summercat.com/tweets/"

// Tweet describe a tweet pulled from the database.
type Tweet struct {
	Nick    string
	Text    string
	Time    time.Time
	TweetID int64
}

// MyConfig holds configuration values.
type MyConfig struct {
	DBUser string
	DBPass string
	DBName string
	DBHost string
	// The number of recent tweets to put in the XML.
	NumTweets uint64
}

// connectToDB opens a new connection to the database.
func connectToDB(name string, user string, pass string, host string) (*sql.DB,
	error) {
	dsn := fmt.Sprintf("user=%s password=%s dbname=%s host=%s", user, pass, name,
		host)

	db, err := sql.Open("postgres", dsn)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to the database: %s", err)
	}

	return db, nil
}

// getTweets retrieves tweets from a database.
func getTweets(config *MyConfig) ([]Tweet, error) {
	db, err := connectToDB(config.DBName, config.DBUser, config.DBPass,
		config.DBHost)
	if err != nil {
		return nil, err
	}

	defer func() {
		err := db.Close()
		if err != nil {
			log.Printf("Database close: %s", err)
		}
	}()

	// get most recent tweets.
	sql := `
SELECT nick, text, time, tweet_id
FROM tweet
ORDER BY time DESC
LIMIT $1
`
	rows, err := db.Query(sql, config.NumTweets)
	if err != nil {
		return nil, fmt.Errorf("query failure: %s", err)
	}

	var tweets []Tweet
	for rows.Next() {
		tweet := Tweet{}

		err = rows.Scan(&tweet.Nick, &tweet.Text, &tweet.Time, &tweet.TweetID)
		if err != nil {
			return nil, fmt.Errorf("failed to scan row: %s", err)
		}

		tweets = append(tweets, tweet)
	}

	return tweets, nil
}

// Create a URL to the status.
//
// Apparently this URL is not in the tweet status payload.
//
// Form: https://twitter.com/<screenname>/status/<tweetid>
func createStatusURL(screenName string, tweetID int64) string {
	return fmt.Sprintf("https://twitter.com/%s/status/%d", screenName, tweetID)
}

func main() {
	log.SetFlags(log.Ltime | log.Llongfile)

	outputFile := flag.String("output-file", "", "Output XML file to write.")
	configFile := flag.String("config-file", "", "Config file")

	flag.Parse()

	if len(*outputFile) == 0 || len(*configFile) == 0 {
		fmt.Println("You must provide a config file.")
		flag.PrintDefaults()
		os.Exit(1)
	}

	var settings MyConfig
	err := config.GetConfig(*configFile, &settings)
	if err != nil {
		log.Fatalf("Failed to retrieve config: %s", err)
	}

	// TODO: We could run validation on each config item.

	gorselib.SetQuiet(true)

	tweets, err := getTweets(&settings)
	if err != nil {
		log.Fatalf("Failed to retrieve tweets: %s", err)
	}

	rss := gorselib.RSSFeed{}
	rss.Name = "Twitreader"
	rss.URI = FeedURI
	rss.Description = "Twitreader tweets"
	rss.LastUpdateTime = time.Now()

	for _, tweet := range tweets {
		item := gorselib.RSSItem{
			Title:           fmt.Sprintf("%s", tweet.Nick),
			URI:             createStatusURL(tweet.Nick, tweet.TweetID),
			Description:     tweet.Text,
			PublicationDate: tweet.Time,
		}
		rss.Items = append(rss.Items, item)
	}

	err = gorselib.WriteFeedXML(&rss, *outputFile)
	if err != nil {
		log.Fatalf("Failed to write XML: %s", err)
	}
}
<|endoftext|>


<commit_before>package actors

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"

	"github.com/cloudfoundry/cli/cf/api/application_bits"
	"github.com/cloudfoundry/cli/cf/api/resources"
	"github.com/cloudfoundry/cli/cf/app_files"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/gofileutils/fileutils"
)

const windowsPathPrefix = `\\?\`

//go:generate counterfeiter -o fakes/fake_push_actor.go . PushActor
type PushActor interface {
	UploadApp(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) error
	ProcessPath(dirOrZipFile string, f func(string)) error
	GatherFiles(localFiles []models.AppFileFields, appDir string, uploadDir string) ([]resources.AppFileResource, bool, error)
}

type PushActorImpl struct {
	appBitsRepo application_bits.ApplicationBitsRepository
	appfiles    app_files.AppFiles
	zipper      app_files.Zipper
}

func NewPushActor(appBitsRepo application_bits.ApplicationBitsRepository, zipper app_files.Zipper, appfiles app_files.AppFiles) PushActor {
	return PushActorImpl{
		appBitsRepo: appBitsRepo,
		appfiles:    appfiles,
		zipper:      zipper,
	}
}

func (actor PushActorImpl) ProcessPath(dirOrZipFile string, f func(string)) error {
	if !actor.zipper.IsZipFile(dirOrZipFile) {
		appDir, err := filepath.EvalSymlinks(dirOrZipFile)
		if err != nil {
			return err
		}

		if filepath.IsAbs(appDir) {
			f(appDir)
		} else {
			var absPath string
			absPath, err = filepath.Abs(appDir)
			if err != nil {
				return err
			}

			f(absPath)
		}

		return nil
	}

	tempDir, err := ioutil.TempDir("", "unzipped-app")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)

	err = actor.zipper.Unzip(dirOrZipFile, tempDir)
	if err != nil {
		return err
	}

	f(tempDir)

	return nil
}

func (actor PushActorImpl) GatherFiles(localFiles []models.AppFileFields, appDir string, uploadDir string) ([]resources.AppFileResource, bool, error) {
	appFileResource := []resources.AppFileResource{}
	for _, file := range localFiles {
		appFileResource = append(appFileResource, resources.AppFileResource{
			Path: file.Path,
			Sha1: file.Sha1,
			Size: file.Size,
		})
	}

	remoteFiles, err := actor.appBitsRepo.GetApplicationFiles(appFileResource)
	if err != nil {
		return []resources.AppFileResource{}, false, err
	}

	filesToUpload := make([]models.AppFileFields, len(localFiles), len(localFiles))
	copy(filesToUpload, localFiles)

	for _, remoteFile := range remoteFiles {
		for i, fileToUpload := range filesToUpload {
			if remoteFile.Path == fileToUpload.Path {
				filesToUpload = append(filesToUpload[:i], filesToUpload[i+1:]...)
			}
		}
	}

	err = actor.appfiles.CopyFiles(filesToUpload, appDir, uploadDir)
	if err != nil {
		return []resources.AppFileResource{}, false, err
	}

	_, err = os.Stat(filepath.Join(appDir, ".cfignore"))
	if err == nil {
		err = fileutils.CopyPathToPath(filepath.Join(appDir, ".cfignore"), filepath.Join(uploadDir, ".cfignore"))
		if err != nil {
			return []resources.AppFileResource{}, false, err
		}
	}

	for i := range remoteFiles {
		fullPath, err := filepath.Abs(filepath.Join(appDir, remoteFiles[i].Path))
		if err != nil {
			return []resources.AppFileResource{}, false, err
		}

		if runtime.GOOS == "windows" {
			fullPath = windowsPathPrefix + fullPath
		}
		fileInfo, err := os.Lstat(fullPath)
		if err != nil {
			return []resources.AppFileResource{}, false, err
		}
		fileMode := fileInfo.Mode()

		if runtime.GOOS == "windows" {
			fileMode = fileMode | 0700
		}

		remoteFiles[i].Mode = fmt.Sprintf("%#o", fileMode)
	}

	return remoteFiles, len(filesToUpload) > 0, nil
}

func (actor PushActorImpl) UploadApp(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) error {
	return actor.appBitsRepo.UploadBits(appGuid, zipFile, presentFiles)
}
<commit_msg>Add explanatory comment to ProcessPath<commit_after>package actors

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"

	"github.com/cloudfoundry/cli/cf/api/application_bits"
	"github.com/cloudfoundry/cli/cf/api/resources"
	"github.com/cloudfoundry/cli/cf/app_files"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/gofileutils/fileutils"
)

const windowsPathPrefix = `\\?\`

//go:generate counterfeiter -o fakes/fake_push_actor.go . PushActor
type PushActor interface {
	UploadApp(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) error
	ProcessPath(dirOrZipFile string, f func(string)) error
	GatherFiles(localFiles []models.AppFileFields, appDir string, uploadDir string) ([]resources.AppFileResource, bool, error)
}

type PushActorImpl struct {
	appBitsRepo application_bits.ApplicationBitsRepository
	appfiles    app_files.AppFiles
	zipper      app_files.Zipper
}

func NewPushActor(appBitsRepo application_bits.ApplicationBitsRepository, zipper app_files.Zipper, appfiles app_files.AppFiles) PushActor {
	return PushActorImpl{
		appBitsRepo: appBitsRepo,
		appfiles:    appfiles,
		zipper:      zipper,
	}
}

// ProcessPath takes in a director of app files or a zip file which contains
// the app files. If given a zip file, it will extract the zip to a temporary
// location, call the provided callback with that location, and then clean up
// the location after the callback has been executed.
//
// This was done so that the caller of ProcessPath wouldn't need to know if it
// was a zip file or an app dir that it was given, and the caller would not be
// responsible for cleaning up the temporary directory ProcessPath creates when
// given a zip.
func (actor PushActorImpl) ProcessPath(dirOrZipFile string, f func(string)) error {
	if !actor.zipper.IsZipFile(dirOrZipFile) {
		appDir, err := filepath.EvalSymlinks(dirOrZipFile)
		if err != nil {
			return err
		}

		if filepath.IsAbs(appDir) {
			f(appDir)
		} else {
			var absPath string
			absPath, err = filepath.Abs(appDir)
			if err != nil {
				return err
			}

			f(absPath)
		}

		return nil
	}

	tempDir, err := ioutil.TempDir("", "unzipped-app")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)

	err = actor.zipper.Unzip(dirOrZipFile, tempDir)
	if err != nil {
		return err
	}

	f(tempDir)

	return nil
}

func (actor PushActorImpl) GatherFiles(localFiles []models.AppFileFields, appDir string, uploadDir string) ([]resources.AppFileResource, bool, error) {
	appFileResource := []resources.AppFileResource{}
	for _, file := range localFiles {
		appFileResource = append(appFileResource, resources.AppFileResource{
			Path: file.Path,
			Sha1: file.Sha1,
			Size: file.Size,
		})
	}

	remoteFiles, err := actor.appBitsRepo.GetApplicationFiles(appFileResource)
	if err != nil {
		return []resources.AppFileResource{}, false, err
	}

	filesToUpload := make([]models.AppFileFields, len(localFiles), len(localFiles))
	copy(filesToUpload, localFiles)

	for _, remoteFile := range remoteFiles {
		for i, fileToUpload := range filesToUpload {
			if remoteFile.Path == fileToUpload.Path {
				filesToUpload = append(filesToUpload[:i], filesToUpload[i+1:]...)
			}
		}
	}

	err = actor.appfiles.CopyFiles(filesToUpload, appDir, uploadDir)
	if err != nil {
		return []resources.AppFileResource{}, false, err
	}

	_, err = os.Stat(filepath.Join(appDir, ".cfignore"))
	if err == nil {
		err = fileutils.CopyPathToPath(filepath.Join(appDir, ".cfignore"), filepath.Join(uploadDir, ".cfignore"))
		if err != nil {
			return []resources.AppFileResource{}, false, err
		}
	}

	for i := range remoteFiles {
		fullPath, err := filepath.Abs(filepath.Join(appDir, remoteFiles[i].Path))
		if err != nil {
			return []resources.AppFileResource{}, false, err
		}

		if runtime.GOOS == "windows" {
			fullPath = windowsPathPrefix + fullPath
		}
		fileInfo, err := os.Lstat(fullPath)
		if err != nil {
			return []resources.AppFileResource{}, false, err
		}
		fileMode := fileInfo.Mode()

		if runtime.GOOS == "windows" {
			fileMode = fileMode | 0700
		}

		remoteFiles[i].Mode = fmt.Sprintf("%#o", fileMode)
	}

	return remoteFiles, len(filesToUpload) > 0, nil
}

func (actor PushActorImpl) UploadApp(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) error {
	return actor.appBitsRepo.UploadBits(appGuid, zipFile, presentFiles)
}
<|endoftext|>


<commit_before>package mdns

/*
	MDNS is a multicast dns registry for service discovery
	This creates a zero dependency system which is great
	where multicast dns is available. This usually depends
	on the ability to leverage udp and multicast/broadcast.
*/

import (
	"net"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/mdns"
	"github.com/micro/go-micro/registry"
	hash "github.com/mitchellh/hashstructure"
)

type mdnsTxt struct {
	Version   string
	Endpoints []*registry.Endpoint
	Metadata  map[string]string
}

type mdnsEntry struct {
	hash uint64
	id   string
	node *mdns.Server
}

type mdnsRegistry struct {
	opts registry.Options

	sync.Mutex
	services map[string][]*mdnsEntry
}

func newRegistry(opts ...registry.Option) registry.Registry {
	options := registry.Options{
		Timeout: time.Millisecond * 100,
	}

	return &mdnsRegistry{
		opts:     options,
		services: make(map[string][]*mdnsEntry),
	}
}

func (m *mdnsRegistry) Register(service *registry.Service, opts ...registry.RegisterOption) error {
	m.Lock()
	defer m.Unlock()

	entries, ok := m.services[service.Name]
	// first entry, create wildcard used for list queries
	if !ok {
		s, err := mdns.NewMDNSService(
			service.Name,
			"_services",
			"",
			"",
			9999,
			[]net.IP{net.ParseIP("0.0.0.0")},
			nil,
		)
		if err != nil {
			return err
		}

		srv, err := mdns.NewServer(&mdns.Config{Zone: s})
		if err != nil {
			return err
		}

		// append the wildcard entry
		entries = append(entries, &mdnsEntry{id: "*", node: srv})
	}

	var gerr error

	for _, node := range service.Nodes {
		// create hash of service; uint64
		h, err := hash.Hash(node, nil)
		if err != nil {
			gerr = err
			continue
		}

		var seen bool
		var e *mdnsEntry

		for _, entry := range entries {
			if node.Id == entry.id {
				seen = true
				e = entry
				break
			}
		}

		// already registered, continue
		if seen && e.hash == h {
			continue
			// hash doesn't match, shutdown
		} else if seen {
			e.node.Shutdown()
			// doesn't exist
		} else {
			e = &mdnsEntry{hash: h}
		}

		txt, err := encode(&mdnsTxt{
			Version:   service.Version,
			Endpoints: service.Endpoints,
			Metadata:  node.Metadata,
		})

		if err != nil {
			gerr = err
			continue
		}

		// we got here, new node
		s, err := mdns.NewMDNSService(
			node.Id,
			service.Name,
			"",
			"",
			node.Port,
			[]net.IP{net.ParseIP(node.Address)},
			txt,
		)
		if err != nil {
			gerr = err
			continue
		}

		srv, err := mdns.NewServer(&mdns.Config{Zone: s})
		if err != nil {
			gerr = err
			continue
		}

		e.id = node.Id
		e.node = srv
		entries = append(entries, e)
	}

	// save
	m.services[service.Name] = entries

	return gerr
}

func (m *mdnsRegistry) Deregister(service *registry.Service) error {
	m.Lock()
	defer m.Unlock()

	var newEntries []*mdnsEntry

	// loop existing entries, check if any match, shutdown those that do
	for _, entry := range m.services[service.Name] {
		var remove bool

		for _, node := range service.Nodes {
			if node.Id == entry.id {
				entry.node.Shutdown()
				remove = true
				break
			}
		}

		// keep it?
		if !remove {
			newEntries = append(newEntries, entry)
		}
	}

	// last entry is the wildcard for list queries. Remove it.
	if len(newEntries) == 1 && newEntries[0].id == "*" {
		newEntries[0].node.Shutdown()
		delete(m.services, service.Name)
	} else {
		m.services[service.Name] = newEntries
	}

	return nil
}

func (m *mdnsRegistry) GetService(service string) ([]*registry.Service, error) {
	p := mdns.DefaultParams(service)
	p.Timeout = m.opts.Timeout
	entryCh := make(chan *mdns.ServiceEntry, 10)
	p.Entries = entryCh

	exit := make(chan bool)
	defer close(exit)

	serviceMap := make(map[string]*registry.Service)

	go func() {
		for {
			select {
			case e := <-entryCh:
				// list record so skip
				if p.Service == "_services" {
					continue
				}

				txt, err := decode(e.InfoFields)
				if err != nil {
					continue
				}

				s, ok := serviceMap[txt.Version]
				if !ok {
					s = &registry.Service{
						Name:      service,
						Version:   txt.Version,
						Endpoints: txt.Endpoints,
					}
				}

				s.Nodes = append(s.Nodes, &registry.Node{
					Id:       strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+"."),
					Address:  e.AddrV4.String(),
					Port:     e.Port,
					Metadata: txt.Metadata,
				})

				serviceMap[txt.Version] = s
			case <-exit:
				return
			}
		}
	}()

	if err := mdns.Query(p); err != nil {
		return nil, err
	}

	// create list and return
	var services []*registry.Service

	for _, service := range serviceMap {
		services = append(services, service)
	}

	return services, nil
}

func (m *mdnsRegistry) ListServices() ([]*registry.Service, error) {
	p := mdns.DefaultParams("_services")
	p.Timeout = m.opts.Timeout
	entryCh := make(chan *mdns.ServiceEntry, 10)
	p.Entries = entryCh

	exit := make(chan bool)
	defer close(exit)

	serviceMap := make(map[string]bool)
	var services []*registry.Service

	go func() {
		for {
			select {
			case e := <-entryCh:
				name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".")
				if !serviceMap[name] {
					serviceMap[name] = true
					services = append(services, &registry.Service{Name: name})
				}
			case <-exit:
				return
			}
		}
	}()

	if err := mdns.Query(p); err != nil {
		return nil, err
	}

	return services, nil
}

func (m *mdnsRegistry) Watch() (registry.Watcher, error) {
	return nil, nil
}

func (m *mdnsRegistry) String() string {
	return "mdns"
}

func NewRegistry(opts ...registry.Option) registry.Registry {
	return newRegistry(opts...)
}
<commit_msg>Use our fork of mdns with all the updates<commit_after>package mdns

/*
	MDNS is a multicast dns registry for service discovery
	This creates a zero dependency system which is great
	where multicast dns is available. This usually depends
	on the ability to leverage udp and multicast/broadcast.
*/

import (
	"net"
	"strings"
	"sync"
	"time"

	"github.com/micro/mdns"
	"github.com/micro/go-micro/registry"
	hash "github.com/mitchellh/hashstructure"
)

type mdnsTxt struct {
	Version   string
	Endpoints []*registry.Endpoint
	Metadata  map[string]string
}

type mdnsEntry struct {
	hash uint64
	id   string
	node *mdns.Server
}

type mdnsRegistry struct {
	opts registry.Options

	sync.Mutex
	services map[string][]*mdnsEntry
}

func newRegistry(opts ...registry.Option) registry.Registry {
	options := registry.Options{
		Timeout: time.Millisecond * 100,
	}

	return &mdnsRegistry{
		opts:     options,
		services: make(map[string][]*mdnsEntry),
	}
}

func (m *mdnsRegistry) Register(service *registry.Service, opts ...registry.RegisterOption) error {
	m.Lock()
	defer m.Unlock()

	entries, ok := m.services[service.Name]
	// first entry, create wildcard used for list queries
	if !ok {
		s, err := mdns.NewMDNSService(
			service.Name,
			"_services",
			"",
			"",
			9999,
			[]net.IP{net.ParseIP("0.0.0.0")},
			nil,
		)
		if err != nil {
			return err
		}

		srv, err := mdns.NewServer(&mdns.Config{Zone: s})
		if err != nil {
			return err
		}

		// append the wildcard entry
		entries = append(entries, &mdnsEntry{id: "*", node: srv})
	}

	var gerr error

	for _, node := range service.Nodes {
		// create hash of service; uint64
		h, err := hash.Hash(node, nil)
		if err != nil {
			gerr = err
			continue
		}

		var seen bool
		var e *mdnsEntry

		for _, entry := range entries {
			if node.Id == entry.id {
				seen = true
				e = entry
				break
			}
		}

		// already registered, continue
		if seen && e.hash == h {
			continue
			// hash doesn't match, shutdown
		} else if seen {
			e.node.Shutdown()
			// doesn't exist
		} else {
			e = &mdnsEntry{hash: h}
		}

		txt, err := encode(&mdnsTxt{
			Version:   service.Version,
			Endpoints: service.Endpoints,
			Metadata:  node.Metadata,
		})

		if err != nil {
			gerr = err
			continue
		}

		// we got here, new node
		s, err := mdns.NewMDNSService(
			node.Id,
			service.Name,
			"",
			"",
			node.Port,
			[]net.IP{net.ParseIP(node.Address)},
			txt,
		)
		if err != nil {
			gerr = err
			continue
		}

		srv, err := mdns.NewServer(&mdns.Config{Zone: s})
		if err != nil {
			gerr = err
			continue
		}

		e.id = node.Id
		e.node = srv
		entries = append(entries, e)
	}

	// save
	m.services[service.Name] = entries

	return gerr
}

func (m *mdnsRegistry) Deregister(service *registry.Service) error {
	m.Lock()
	defer m.Unlock()

	var newEntries []*mdnsEntry

	// loop existing entries, check if any match, shutdown those that do
	for _, entry := range m.services[service.Name] {
		var remove bool

		for _, node := range service.Nodes {
			if node.Id == entry.id {
				entry.node.Shutdown()
				remove = true
				break
			}
		}

		// keep it?
		if !remove {
			newEntries = append(newEntries, entry)
		}
	}

	// last entry is the wildcard for list queries. Remove it.
	if len(newEntries) == 1 && newEntries[0].id == "*" {
		newEntries[0].node.Shutdown()
		delete(m.services, service.Name)
	} else {
		m.services[service.Name] = newEntries
	}

	return nil
}

func (m *mdnsRegistry) GetService(service string) ([]*registry.Service, error) {
	p := mdns.DefaultParams(service)
	p.Timeout = m.opts.Timeout
	entryCh := make(chan *mdns.ServiceEntry, 10)
	p.Entries = entryCh

	exit := make(chan bool)
	defer close(exit)

	serviceMap := make(map[string]*registry.Service)

	go func() {
		for {
			select {
			case e := <-entryCh:
				// list record so skip
				if p.Service == "_services" {
					continue
				}

				txt, err := decode(e.InfoFields)
				if err != nil {
					continue
				}

				s, ok := serviceMap[txt.Version]
				if !ok {
					s = &registry.Service{
						Name:      service,
						Version:   txt.Version,
						Endpoints: txt.Endpoints,
					}
				}

				s.Nodes = append(s.Nodes, &registry.Node{
					Id:       strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+"."),
					Address:  e.AddrV4.String(),
					Port:     e.Port,
					Metadata: txt.Metadata,
				})

				serviceMap[txt.Version] = s
			case <-exit:
				return
			}
		}
	}()

	if err := mdns.Query(p); err != nil {
		return nil, err
	}

	// create list and return
	var services []*registry.Service

	for _, service := range serviceMap {
		services = append(services, service)
	}

	return services, nil
}

func (m *mdnsRegistry) ListServices() ([]*registry.Service, error) {
	p := mdns.DefaultParams("_services")
	p.Timeout = m.opts.Timeout
	entryCh := make(chan *mdns.ServiceEntry, 10)
	p.Entries = entryCh

	exit := make(chan bool)
	defer close(exit)

	serviceMap := make(map[string]bool)
	var services []*registry.Service

	go func() {
		for {
			select {
			case e := <-entryCh:
				name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".")
				if !serviceMap[name] {
					serviceMap[name] = true
					services = append(services, &registry.Service{Name: name})
				}
			case <-exit:
				return
			}
		}
	}()

	if err := mdns.Query(p); err != nil {
		return nil, err
	}

	return services, nil
}

func (m *mdnsRegistry) Watch() (registry.Watcher, error) {
	return nil, nil
}

func (m *mdnsRegistry) String() string {
	return "mdns"
}

func NewRegistry(opts ...registry.Option) registry.Registry {
	return newRegistry(opts...)
}
<|endoftext|>


<commit_before>package tiff

import (
	"encoding/binary"
	"fmt"
	"io"

	"sync"
)

// ReadAtReadSeeker is the interface that wraps the Read, ReadAt, and Seek
// methods.  Typical use cases would satisfy this with a bytes.Reader (in
// memory) or an os.File (on disk).  For truly large files, such as BigTIFF, a
// user may want to create a custom solution that combines both in memory and on
// disk solutions for accessing the contents.
type ReadAtReadSeeker interface {
	io.ReadSeeker
	io.ReaderAt
}

// buffer buffers an io.Reader to satisfy ReadAtReadSeeker.  Seeking from the
// end is not supported.  This should be okay since this is for internal use
// only.
type buffer struct {
	mu  sync.Mutex
	r   io.Reader
	pos int
	buf []byte
}

// fill reads data from b.r until the buffer contains at least end bytes.
func (b *buffer) fill(end int) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	m := len(b.buf)
	if end > m {
		if end > cap(b.buf) {
			newcap := 6144
			for newcap < end {
				newcap *= 2
			}
			newbuf := make([]byte, end, newcap)
			copy(newbuf, b.buf)
			b.buf = newbuf
		} else {
			b.buf = b.buf[:end]
		}
		if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil {
			end = m + n
			b.buf = b.buf[:end]
			return err
		}
	}
	return nil
}

func (b *buffer) ReadAt(p []byte, off int64) (int, error) {
	o := int(off)
	end := o + len(p)
	if int64(end) != off+int64(len(p)) {
		return 0, io.ErrUnexpectedEOF
	}

	err := b.fill(end)
	return copy(p, b.buf[o:end]), err
}

func (b *buffer) Read(p []byte) (int, error) {
	end := b.pos + len(p)
	err := b.fill(end)
	return copy(p, b.buf[b.pos:end]), err
}

func (b *buffer) Seek(offset int64, whence int) (int64, error) {
	var newPos int
	// In this package, we only plan to support cases 0 & 1 with case 0
	// being the default and case 1 explicit option.  Case 2 would require
	// loading the entire contents into memory or trying to assert b.r as
	// an *os.File or an io.Seeker.
	switch whence {
	case 1:
		newPos = b.pos + int(offset)
	case 2:
		return 0, fmt.Errorf("tiff: seeking from the end of file is not supported")
	default:
		newPos = int(offset)
	}

	// TODO: Make sure that offset was not a value that can only be
	// expressed as an int64.  This is only of concern for 32 bit systems.

	err := b.fill(newPos)
	if newPos > len(b.buf) {
		b.pos = len(b.buf)
	} else {
		b.pos = newPos
	}
	return int64(b.pos), err
}

// Section returns b as an io.SectionReader to allow access to a specific chunk
// in the buffer.
func (b *buffer) Section(off, n int) *io.SectionReader {
	return io.NewSectionReader(b, int64(off), int64(n))
}

// NewReadAtReadSeeker converts an io.Reader into a ReadAtReadSeeker.
func NewReadAtReadSeeker(r io.Reader) ReadAtReadSeeker {
	if rars, ok := r.(ReadAtReadSeeker); ok {
		return rars
	}
	return &buffer{
		r:   r,
		pos: 0,
		buf: make([]byte, 0, 3072),
	}
}

// BReader wraps a ReadAtReadSeeker with a specific binary.ByteOrder.
type BReader interface {
	BRead(data interface{}) error
	BReadSection(data interface{}, offset int64, n int64) error
	Order() binary.ByteOrder
	ReadAtReadSeeker
}

func NewBReader(r ReadAtReadSeeker, o binary.ByteOrder) BReader {
	return &bReader{order: o, r: r}
}

// bReader wraps a ReadAtReadSeeker and reads it with a specific
// binary.ByteOrder.
type bReader struct {
	order binary.ByteOrder
	r     ReadAtReadSeeker
}

func (b *bReader) Read(p []byte) (n int, err error) {
	return b.r.Read(p)
}

func (b *bReader) ReadAt(p []byte, off int64) (n int, err error) {
	return b.r.ReadAt(p, off)
}

func (b *bReader) BRead(data interface{}) error {
	return binary.Read(b.r, b.order, data)
}

func (b *bReader) BReadSection(data interface{}, offset int64, n int64) error {
	if offset < 0 {
		return fmt.Errorf("tiff: invalid offset %d", offset)
	}
	if n < 1 {
		return fmt.Errorf("tiff: invalid section size %d", n)
	}
	sr := io.NewSectionReader(b.r, offset, n)
	return binary.Read(sr, b.order, data)
}

func (b *bReader) Seek(offset int64, whence int) (int64, error) {
	return b.r.Seek(offset, whence)
}

func (b *bReader) Order() binary.ByteOrder {
	return b.order
}

type uint16Slice []uint16

func (p uint16Slice) Len() int           { return len(p) }
func (p uint16Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint16Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
<commit_msg>Fix buffer.Read to do the correct thing.  Fixed up the comment for NewReadAtReadSeeker.<commit_after>package tiff

import (
	"encoding/binary"
	"fmt"
	"io"

	"sync"
)

// ReadAtReadSeeker is the interface that wraps the Read, ReadAt, and Seek
// methods.  Typical use cases would satisfy this with a bytes.Reader (in
// memory) or an os.File (on disk).  For truly large files, such as BigTIFF, a
// user may want to create a custom solution that combines both in memory and on
// disk solutions for accessing the contents.
type ReadAtReadSeeker interface {
	io.ReadSeeker
	io.ReaderAt
}

// buffer buffers an io.Reader to satisfy ReadAtReadSeeker.  Seeking from the
// end is not supported.  This should be okay since this is for internal use
// only.
type buffer struct {
	mu  sync.Mutex
	r   io.Reader
	pos int
	buf []byte
}

// fill reads data from b.r until the buffer contains at least end bytes.
func (b *buffer) fill(end int) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	m := len(b.buf)
	if end > m {
		if end > cap(b.buf) {
			newcap := 6144
			for newcap < end {
				newcap *= 2
			}
			newbuf := make([]byte, end, newcap)
			copy(newbuf, b.buf)
			b.buf = newbuf
		} else {
			b.buf = b.buf[:end]
		}
		if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil {
			end = m + n
			b.buf = b.buf[:end]
			return err
		}
	}
	return nil
}

func (b *buffer) ReadAt(p []byte, off int64) (int, error) {
	o := int(off)
	end := o + len(p)
	if int64(end) != off+int64(len(p)) {
		return 0, io.ErrUnexpectedEOF
	}

	err := b.fill(end)
	return copy(p, b.buf[o:end]), err
}

func (b *buffer) Read(p []byte) (int, error) {
	end := b.pos + len(p)
	if err := b.fill(end); err != nil {
		return 0, err
	}
	n := copy(p, b.buf[b.pos:end])
	b.pos = end
	return n, nil
}

func (b *buffer) Seek(offset int64, whence int) (int64, error) {
	var newPos int
	// In this package, we only plan to support cases 0 & 1 with case 0
	// being the default and case 1 explicit option.  Case 2 would require
	// loading the entire contents into memory or trying to assert b.r as
	// an *os.File or an io.Seeker.
	switch whence {
	case 1:
		newPos = b.pos + int(offset)
	case 2:
		return 0, fmt.Errorf("tiff: seeking from the end of file is not supported")
	default:
		newPos = int(offset)
	}

	// TODO: Make sure that offset was not a value that can only be
	// expressed as an int64.  This is only of concern for 32 bit systems.

	err := b.fill(newPos)
	if newPos > len(b.buf) {
		b.pos = len(b.buf)
	} else {
		b.pos = newPos
	}
	return int64(b.pos), err
}

// Section returns b as an io.SectionReader to allow access to a specific chunk
// in the buffer.
func (b *buffer) Section(off, n int) *io.SectionReader {
	return io.NewSectionReader(b, int64(off), int64(n))
}

// NewReadAtReadSeeker converts r (an io.Reader) into a ReadAtReadSeeker.  If
// the underlying type of r can satisfy a ReadAtReadSeeker, it is asserted as
// such and used directly instead of being wrapped.
func NewReadAtReadSeeker(r io.Reader) ReadAtReadSeeker {
	if rars, ok := r.(ReadAtReadSeeker); ok {
		return rars
	}
	return &buffer{
		r:   r,
		pos: 0,
		buf: make([]byte, 0, 3072),
	}
}

// BReader wraps a ReadAtReadSeeker with a specific binary.ByteOrder.
type BReader interface {
	BRead(data interface{}) error
	BReadSection(data interface{}, offset int64, n int64) error
	Order() binary.ByteOrder
	ReadAtReadSeeker
}

func NewBReader(r ReadAtReadSeeker, o binary.ByteOrder) BReader {
	return &bReader{order: o, r: r}
}

// bReader wraps a ReadAtReadSeeker and reads it with a specific
// binary.ByteOrder.
type bReader struct {
	order binary.ByteOrder
	r     ReadAtReadSeeker
}

func (b *bReader) Read(p []byte) (n int, err error) {
	return b.r.Read(p)
}

func (b *bReader) ReadAt(p []byte, off int64) (n int, err error) {
	return b.r.ReadAt(p, off)
}

func (b *bReader) BRead(data interface{}) error {
	return binary.Read(b.r, b.order, data)
}

func (b *bReader) BReadSection(data interface{}, offset int64, n int64) error {
	if offset < 0 {
		return fmt.Errorf("tiff: invalid offset %d", offset)
	}
	if n < 1 {
		return fmt.Errorf("tiff: invalid section size %d", n)
	}
	sr := io.NewSectionReader(b.r, offset, n)
	return binary.Read(sr, b.order, data)
}

func (b *bReader) Seek(offset int64, whence int) (int64, error) {
	return b.r.Seek(offset, whence)
}

func (b *bReader) Order() binary.ByteOrder {
	return b.order
}

type uint16Slice []uint16

func (p uint16Slice) Len() int           { return len(p) }
func (p uint16Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint16Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
<|endoftext|>


<commit_before>package fsrepo

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	repo "github.com/jbenet/go-ipfs/repo"
	common "github.com/jbenet/go-ipfs/repo/common"
	config "github.com/jbenet/go-ipfs/repo/config"
	lockfile "github.com/jbenet/go-ipfs/repo/fsrepo/lock"
	opener "github.com/jbenet/go-ipfs/repo/fsrepo/opener"
	util "github.com/jbenet/go-ipfs/util"
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
)

var (
	// openerCounter prevents the fsrepo from being removed while there exist open
	// FSRepo handles. It also ensures that the Init is atomic.
	//
	// packageLock also protects numOpenedRepos
	//
	// If an operation is used when repo is Open and the operation does not
	// change the repo's state, the package lock does not need to be acquired.
	openerCounter *opener.Counter

	lockfiles map[string]io.Closer
)

func init() {
	openerCounter = opener.NewCounter()
	lockfiles = make(map[string]io.Closer)
}

// FSRepo represents an IPFS FileSystem Repo. It is not thread-safe.
type FSRepo struct {
	state  state
	path   string
	config *config.Config
}

// At returns a handle to an FSRepo at the provided |path|.
func At(path string) *FSRepo {
	// This method must not have side-effects.
	return &FSRepo{
		path:  path,
		state: unopened, // explicitly set for clarity
	}
}

// Init initializes a new FSRepo at the given path with the provided config.
func Init(path string, conf *config.Config) error {
	openerCounter.Lock() // lock must be held to ensure atomicity (prevent Removal)
	defer openerCounter.Unlock()

	if isInitializedUnsynced(path) {
		return nil
	}
	configFilename, err := config.Filename(path)
	if err != nil {
		return err
	}
	if err := writeConfigFile(configFilename, conf); err != nil {
		return err
	}
	return nil
}

// Remove recursively removes the FSRepo at |path|.
func Remove(path string) error {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	if openerCounter.NumOpeners(path) != 0 {
		return errors.New("repo in use")
	}
	return os.RemoveAll(path)
}

// LockedByOtherProcess returns true if the FSRepo is locked by another
// process. If true, then the repo cannot be opened by this process.
func LockedByOtherProcess(repoPath string) bool {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	// NB: the lock is only held when repos are Open
	return lockfile.Locked(repoPath) && openerCounter.NumOpeners(repoPath) == 0
}

// Open returns an error if the repo is not initialized.
func (r *FSRepo) Open() error {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	if r.state != unopened {
		return debugerror.Errorf("repo is %s", r.state)
	}
	if !isInitializedUnsynced(r.path) {
		return debugerror.New("ipfs not initialized, please run 'ipfs init'")
	}
	// check repo path, then check all constituent parts.
	// TODO acquire repo lock
	// TODO if err := initCheckDir(logpath); err != nil { // }
	if err := initCheckDir(r.path); err != nil {
		return err
	}

	configFilename, err := config.Filename(r.path)
	if err != nil {
		return err
	}
	conf, err := load(configFilename)
	if err != nil {
		return err
	}
	r.config = conf

	// datastore
	dspath, err := config.DataStorePath("")
	if err != nil {
		return err
	}
	if err := initCheckDir(dspath); err != nil {
		return debugerror.Errorf("datastore: %s", err)
	}

	logpath, err := config.LogsPath("")
	if err != nil {
		return debugerror.Wrap(err)
	}
	if err := initCheckDir(logpath); err != nil {
		return debugerror.Errorf("logs: %s", err)
	}

	return transitionToOpened(r)
}

// Config returns the FSRepo's config. This method must not be called if the
// repo is not open.
//
// Result when not Open is undefined. The method may panic if it pleases.
func (r *FSRepo) Config() *config.Config {
	// no lock necessary because repo is either Open (and thus protected from
	// Removal) or has no side-effect
	if r.state != opened {
		panic(fmt.Sprintln("repo is", r.state))
	}
	return r.config
}

// SetConfig updates the FSRepo's config.
func (r *FSRepo) SetConfig(updated *config.Config) error {
	// no lock required because repo should be Open
	if r.state != opened {
		panic(fmt.Sprintln("repo is", r.state))
	}
	configFilename, err := config.Filename(r.path)
	if err != nil {
		return err
	}
	// to avoid clobbering user-provided keys, must read the config from disk
	// as a map, write the updated struct values to the map and write the map
	// to disk.
	var mapconf map[string]interface{}
	if err := readConfigFile(configFilename, &mapconf); err != nil {
		return err
	}
	m, err := config.ToMap(updated)
	if err != nil {
		return err
	}
	for k, v := range m {
		mapconf[k] = v
	}
	if err := writeConfigFile(configFilename, mapconf); err != nil {
		return err
	}
	*r.config = *updated // copy so caller cannot modify this private config
	return nil
}

// GetConfigKey retrieves only the value of a particular key.
func (r *FSRepo) GetConfigKey(key string) (interface{}, error) {
	if r.state != opened {
		return nil, debugerror.Errorf("repo is %s", r.state)
	}
	filename, err := config.Filename(r.path)
	if err != nil {
		return nil, err
	}
	var cfg map[string]interface{}
	if err := readConfigFile(filename, &cfg); err != nil {
		return nil, err
	}
	return common.MapGetKV(cfg, key)
}

// SetConfigKey writes the value of a particular key.
func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
	// no lock required because repo should be Open
	if r.state != opened {
		return debugerror.Errorf("repo is %s", r.state)
	}
	filename, err := config.Filename(r.path)
	if err != nil {
		return err
	}
	var mapconf map[string]interface{}
	if err := readConfigFile(filename, &mapconf); err != nil {
		return err
	}
	if err := common.MapSetKV(mapconf, key, value); err != nil {
		return err
	}
	if err := writeConfigFile(filename, mapconf); err != nil {
		return err
	}
	conf, err := config.FromMap(mapconf)
	if err != nil {
		return err
	}
	return r.SetConfig(conf)
}

// Close closes the FSRepo, releasing held resources.
func (r *FSRepo) Close() error {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	if r.state != opened {
		return debugerror.Errorf("repo is %s", r.state)
	}
	return transitionToClosed(r)
}

var _ io.Closer = &FSRepo{}
var _ repo.Repo = &FSRepo{}

// IsInitialized returns true if the repo is initialized at provided |path|.
func IsInitialized(path string) bool {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	return isInitializedUnsynced(path)
}

// isInitializedUnsynced reports whether the repo is initialized. Caller must
// hold openerCounter lock.
func isInitializedUnsynced(path string) bool {
	configFilename, err := config.Filename(path)
	if err != nil {
		return false
	}
	if !util.FileExists(configFilename) {
		return false
	}
	return true
}

// initCheckDir ensures the directory exists and is writable
func initCheckDir(path string) error {
	// Construct the path if missing
	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		return err
	}
	// Check the directory is writeable
	if f, err := os.Create(filepath.Join(path, "._check_writeable")); err == nil {
		os.Remove(f.Name())
	} else {
		return debugerror.New("'" + path + "' is not writeable")
	}
	return nil
}

// transitionToOpened manages the state transition to |opened|. Caller must hold
// openerCounter lock.
func transitionToOpened(r *FSRepo) error {
	r.state = opened
	if countBefore := openerCounter.NumOpeners(r.path); countBefore == 0 { // #first
		closer, err := lockfile.Lock(r.path)
		if err != nil {
			return err
		}
		lockfiles[r.path] = closer
	}
	return openerCounter.AddOpener(r.path)
}

// transitionToClosed manages the state transition to |closed|. Caller must
// hold openerCounter lock.
func transitionToClosed(r *FSRepo) error {
	r.state = closed
	if err := openerCounter.RemoveOpener(r.path); err != nil {
		return err
	}
	if countAfter := openerCounter.NumOpeners(r.path); countAfter == 0 {
		closer, ok := lockfiles[r.path]
		if !ok {
			return errors.New("package error: lockfile is not held")
		}
		if err := closer.Close(); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>fix(repo): clean the path before using it<commit_after>package fsrepo

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"

	repo "github.com/jbenet/go-ipfs/repo"
	common "github.com/jbenet/go-ipfs/repo/common"
	config "github.com/jbenet/go-ipfs/repo/config"
	lockfile "github.com/jbenet/go-ipfs/repo/fsrepo/lock"
	opener "github.com/jbenet/go-ipfs/repo/fsrepo/opener"
	util "github.com/jbenet/go-ipfs/util"
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
)

var (
	// openerCounter prevents the fsrepo from being removed while there exist open
	// FSRepo handles. It also ensures that the Init is atomic.
	//
	// packageLock also protects numOpenedRepos
	//
	// If an operation is used when repo is Open and the operation does not
	// change the repo's state, the package lock does not need to be acquired.
	openerCounter *opener.Counter

	lockfiles map[string]io.Closer
)

func init() {
	openerCounter = opener.NewCounter()
	lockfiles = make(map[string]io.Closer)
}

// FSRepo represents an IPFS FileSystem Repo. It is not thread-safe.
type FSRepo struct {
	state  state
	path   string
	config *config.Config
}

// At returns a handle to an FSRepo at the provided |path|.
func At(repoPath string) *FSRepo {
	// This method must not have side-effects.
	return &FSRepo{
		path:  path.Clean(repoPath),
		state: unopened, // explicitly set for clarity
	}
}

// Init initializes a new FSRepo at the given path with the provided config.
func Init(path string, conf *config.Config) error {
	openerCounter.Lock() // lock must be held to ensure atomicity (prevent Removal)
	defer openerCounter.Unlock()

	if isInitializedUnsynced(path) {
		return nil
	}
	configFilename, err := config.Filename(path)
	if err != nil {
		return err
	}
	if err := writeConfigFile(configFilename, conf); err != nil {
		return err
	}
	return nil
}

// Remove recursively removes the FSRepo at |path|.
func Remove(path string) error {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	if openerCounter.NumOpeners(path) != 0 {
		return errors.New("repo in use")
	}
	return os.RemoveAll(path)
}

// LockedByOtherProcess returns true if the FSRepo is locked by another
// process. If true, then the repo cannot be opened by this process.
func LockedByOtherProcess(repoPath string) bool {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	// NB: the lock is only held when repos are Open
	return lockfile.Locked(repoPath) && openerCounter.NumOpeners(repoPath) == 0
}

// Open returns an error if the repo is not initialized.
func (r *FSRepo) Open() error {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	if r.state != unopened {
		return debugerror.Errorf("repo is %s", r.state)
	}
	if !isInitializedUnsynced(r.path) {
		return debugerror.New("ipfs not initialized, please run 'ipfs init'")
	}
	// check repo path, then check all constituent parts.
	// TODO acquire repo lock
	// TODO if err := initCheckDir(logpath); err != nil { // }
	if err := initCheckDir(r.path); err != nil {
		return err
	}

	configFilename, err := config.Filename(r.path)
	if err != nil {
		return err
	}
	conf, err := load(configFilename)
	if err != nil {
		return err
	}
	r.config = conf

	// datastore
	dspath, err := config.DataStorePath("")
	if err != nil {
		return err
	}
	if err := initCheckDir(dspath); err != nil {
		return debugerror.Errorf("datastore: %s", err)
	}

	logpath, err := config.LogsPath("")
	if err != nil {
		return debugerror.Wrap(err)
	}
	if err := initCheckDir(logpath); err != nil {
		return debugerror.Errorf("logs: %s", err)
	}

	return transitionToOpened(r)
}

// Config returns the FSRepo's config. This method must not be called if the
// repo is not open.
//
// Result when not Open is undefined. The method may panic if it pleases.
func (r *FSRepo) Config() *config.Config {
	// no lock necessary because repo is either Open (and thus protected from
	// Removal) or has no side-effect
	if r.state != opened {
		panic(fmt.Sprintln("repo is", r.state))
	}
	return r.config
}

// SetConfig updates the FSRepo's config.
func (r *FSRepo) SetConfig(updated *config.Config) error {
	// no lock required because repo should be Open
	if r.state != opened {
		panic(fmt.Sprintln("repo is", r.state))
	}
	configFilename, err := config.Filename(r.path)
	if err != nil {
		return err
	}
	// to avoid clobbering user-provided keys, must read the config from disk
	// as a map, write the updated struct values to the map and write the map
	// to disk.
	var mapconf map[string]interface{}
	if err := readConfigFile(configFilename, &mapconf); err != nil {
		return err
	}
	m, err := config.ToMap(updated)
	if err != nil {
		return err
	}
	for k, v := range m {
		mapconf[k] = v
	}
	if err := writeConfigFile(configFilename, mapconf); err != nil {
		return err
	}
	*r.config = *updated // copy so caller cannot modify this private config
	return nil
}

// GetConfigKey retrieves only the value of a particular key.
func (r *FSRepo) GetConfigKey(key string) (interface{}, error) {
	if r.state != opened {
		return nil, debugerror.Errorf("repo is %s", r.state)
	}
	filename, err := config.Filename(r.path)
	if err != nil {
		return nil, err
	}
	var cfg map[string]interface{}
	if err := readConfigFile(filename, &cfg); err != nil {
		return nil, err
	}
	return common.MapGetKV(cfg, key)
}

// SetConfigKey writes the value of a particular key.
func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
	// no lock required because repo should be Open
	if r.state != opened {
		return debugerror.Errorf("repo is %s", r.state)
	}
	filename, err := config.Filename(r.path)
	if err != nil {
		return err
	}
	var mapconf map[string]interface{}
	if err := readConfigFile(filename, &mapconf); err != nil {
		return err
	}
	if err := common.MapSetKV(mapconf, key, value); err != nil {
		return err
	}
	if err := writeConfigFile(filename, mapconf); err != nil {
		return err
	}
	conf, err := config.FromMap(mapconf)
	if err != nil {
		return err
	}
	return r.SetConfig(conf)
}

// Close closes the FSRepo, releasing held resources.
func (r *FSRepo) Close() error {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	if r.state != opened {
		return debugerror.Errorf("repo is %s", r.state)
	}
	return transitionToClosed(r)
}

var _ io.Closer = &FSRepo{}
var _ repo.Repo = &FSRepo{}

// IsInitialized returns true if the repo is initialized at provided |path|.
func IsInitialized(path string) bool {
	openerCounter.Lock()
	defer openerCounter.Unlock()
	return isInitializedUnsynced(path)
}

// isInitializedUnsynced reports whether the repo is initialized. Caller must
// hold openerCounter lock.
func isInitializedUnsynced(path string) bool {
	configFilename, err := config.Filename(path)
	if err != nil {
		return false
	}
	if !util.FileExists(configFilename) {
		return false
	}
	return true
}

// initCheckDir ensures the directory exists and is writable
func initCheckDir(path string) error {
	// Construct the path if missing
	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		return err
	}
	// Check the directory is writeable
	if f, err := os.Create(filepath.Join(path, "._check_writeable")); err == nil {
		os.Remove(f.Name())
	} else {
		return debugerror.New("'" + path + "' is not writeable")
	}
	return nil
}

// transitionToOpened manages the state transition to |opened|. Caller must hold
// openerCounter lock.
func transitionToOpened(r *FSRepo) error {
	r.state = opened
	if countBefore := openerCounter.NumOpeners(r.path); countBefore == 0 { // #first
		closer, err := lockfile.Lock(r.path)
		if err != nil {
			return err
		}
		lockfiles[r.path] = closer
	}
	return openerCounter.AddOpener(r.path)
}

// transitionToClosed manages the state transition to |closed|. Caller must
// hold openerCounter lock.
func transitionToClosed(r *FSRepo) error {
	r.state = closed
	if err := openerCounter.RemoveOpener(r.path); err != nil {
		return err
	}
	if countAfter := openerCounter.NumOpeners(r.path); countAfter == 0 {
		closer, ok := lockfiles[r.path]
		if !ok {
			return errors.New("package error: lockfile is not held")
		}
		if err := closer.Close(); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package ice

import (
	"net"
	"time"
)

const (
	receiveMTU             = 8192
	defaultLocalPreference = 65535

	// ComponentRTP indicates that the candidate is used for RTP
	ComponentRTP uint16 = 1
	// ComponentRTCP indicates that the candidate is used for RTCP
	ComponentRTCP
)

// Candidate represents an ICE candidate
type Candidate interface {
	start(a *Agent, conn net.PacketConn)
	addr() net.Addr

	setLastSent(t time.Time)
	seen(outbound bool)
	LastSent() time.Time
	setLastReceived(t time.Time)
	LastReceived() time.Time
	String() string
	Equal(other Candidate) bool
	Priority() uint32
	writeTo(raw []byte, dst Candidate) (int, error)
	close() error

	IP() net.IP
	Port() int
	Component() uint16
	NetworkType() NetworkType

	Type() CandidateType
	RelatedAddress() *CandidateRelatedAddress
}
<commit_msg>Cleanup Candidate interface<commit_after>package ice

import (
	"net"
	"time"
)

const (
	receiveMTU             = 8192
	defaultLocalPreference = 65535

	// ComponentRTP indicates that the candidate is used for RTP
	ComponentRTP uint16 = 1
	// ComponentRTCP indicates that the candidate is used for RTCP
	ComponentRTCP
)

// Candidate represents an ICE candidate
type Candidate interface {
	Component() uint16
	IP() net.IP
	LastReceived() time.Time
	LastSent() time.Time
	NetworkType() NetworkType
	Port() int
	Priority() uint32
	RelatedAddress() *CandidateRelatedAddress
	String() string
	Type() CandidateType

	Equal(other Candidate) bool

	addr() net.Addr

	close() error
	seen(outbound bool)
	start(a *Agent, conn net.PacketConn)
	writeTo(raw []byte, dst Candidate) (int, error)
}
<|endoftext|>


<commit_before>package vcard

import (
	"reflect"
	"testing"
	"time"
)

var testCard = Card{
	"VERSION": []*Field{{Value: "4.0"}},
	"UID":     []*Field{{Value: "urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1"}},
	"FN": []*Field{{
		Value:  "J. Doe",
		Params: Params{"PID": {"1.1"}},
	}},
	"N": []*Field{{Value: "Doe;J.;;;"}},
	"EMAIL": []*Field{{
		Value:  "jdoe@example.com",
		Params: Params{"PID": {"1.1"}},
	}},
	"CLIENTPIDMAP": []*Field{{Value: "1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556"}},
}

var testCardHandmade = Card{
	"VERSION": []*Field{{Value: "4.0"}},
	"N":       []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN":      []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value:  "me@joebloggs.com",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"TEL": []*Field{{
		Value:  "tel:+44 20 1234 5678",
		Params: Params{"TYPE": {"\"cell", "home\""}, "PREF": {"1"}},
	}},
	"ADR": []*Field{{
		Value:  ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"URL": []*Field{{
		Value:  "http://joebloggs.com",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"IMPP": []*Field{{
		Value:  "skype:joe.bloggs",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"X-SOCIALPROFILE": []*Field{{
		Value:  "twitter:https://twitter.com/joebloggs",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
}

var testCardGoogle = Card{
	"VERSION": []*Field{{Value: "3.0"}},
	"N":       []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN":      []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value:  "me@joebloggs.com",
		Params: Params{"TYPE": {"INTERNET", "HOME"}},
	}},
	"TEL": []*Field{{
		Value:  "+44 20 1234 5678",
		Params: Params{"TYPE": {"CELL"}},
	}},
	"ADR": []*Field{{
		Value:  ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: Params{"TYPE": {"HOME"}},
	}},
	"URL": []*Field{
		{Value: "http\\://joebloggs.com", Group: "item1"},
		{Value: "http\\://twitter.com/test", Group: "item2"},
	},
	"X-SKYPE": []*Field{{Value: "joe.bloggs"}},
	"X-ABLABEL": []*Field{
		{Value: "_$!<HomePage>!$_", Group: "item1"},
		{Value: "Twitter", Group: "item2"},
	},
}

var testCardApple = Card{
	"VERSION": []*Field{{Value: "3.0"}},
	"N":       []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN":      []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value:  "me@joebloggs.com",
		Params: Params{"TYPE": {"INTERNET", "HOME", "pref"}},
	}},
	"TEL": []*Field{{
		Value:  "+44 20 1234 5678",
		Params: Params{"TYPE": {"CELL", "VOICE", "pref"}},
	}},
	"ADR": []*Field{{
		Value:  ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: Params{"TYPE": {"HOME", "pref"}},
	}},
	"URL": []*Field{{
		Value:  "http://joebloggs.com",
		Params: Params{"TYPE": {"pref"}},
		Group:  "item1",
	}},
	"X-ABLABEL": []*Field{
		{Value: "_$!<HomePage>!$_", Group: "item1"},
	},
	"IMPP": []*Field{{
		Value:  "skype:joe.bloggs",
		Params: Params{"X-SERVICE-TYPE": {"Skype"}, "TYPE": {"HOME", "pref"}},
	}},
	"X-SOCIALPROFILE": []*Field{{
		Value:  "https://twitter.com/joebloggs",
		Params: Params{"TYPE": {"twitter"}},
	}},
}

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

	expected := []string{"a", "b", "c", "", ""}
	for i, exp := range expected {
		if v := maybeGet(l, i); v != exp {
			t.Errorf("maybeGet(l, %v): expected %q but got %q", i, exp, v)
		}
	}
}

func TestCard(t *testing.T) {
	testCardFullName := testCard["FN"][0]
	if field := testCard.Get(FieldFormattedName); testCardFullName != field {
		t.Errorf("Expected card FN field to be %+v but got %+v", testCardFullName, field)
	}
	if v := testCard.Value(FieldFormattedName); v != testCardFullName.Value {
		t.Errorf("Expected card FN field to be %q but got %q", testCardFullName.Value, v)
	}

	if field := testCard.Get("X-IDONTEXIST"); field != nil {
		t.Errorf("Expected card X-IDONTEXIST field to be %+v but got %+v", nil, field)
	}
	if v := testCard.Value("X-IDONTEXIST"); v != "" {
		t.Errorf("Expected card X-IDONTEXIST field value to be %q but got %q", "", v)
	}

	cardMultipleValues := Card{
		"EMAIL": []*Field{
			{Value: "me@example.org", Params: Params{"TYPE": {"home"}}},
			{Value: "me@example.com", Params: Params{"TYPE": {"work"}}},
		},
	}
	expected := []string{"me@example.org", "me@example.com"}
	if values := cardMultipleValues.Values(FieldEmail); !reflect.DeepEqual(expected, values) {
		t.Errorf("Expected card emails to be %+v but got %+v", expected, values)
	}
	if values := cardMultipleValues.Values("X-IDONTEXIST"); values != nil {
		t.Errorf("Expected card X-IDONTEXIST values to be %+v but got %+v", nil, values)
	}
}

func TestCard_AddValue(t *testing.T) {
	card := make(Card)

	name1 := "Akiyama Mio"
	card.AddValue("FN", name1)
	if values := card.Values("FN"); len(values) != 1 || values[0] != name1 {
		t.Errorf("Expected one FN value, got %v", values)
	}

	name2 := "Mio Akiyama"
	card.AddValue("FN", name2)
	if values := card.Values("FN"); len(values) != 2 || values[0] != name1 || values[1] != name2 {
		t.Errorf("Expected two FN values, got %v", values)
	}
}

func TestCard_Preferred(t *testing.T) {
	if pref := testCard.Preferred("X-IDONTEXIST"); pref != nil {
		t.Errorf("Expected card preferred X-IDONTEXIST field to be %+v but got %+v", nil, pref)
	}
	if v := testCard.PreferredValue("X-IDONTEXIST"); v != "" {
		t.Errorf("Expected card preferred X-IDONTEXIST field value to be %q but got %q", "", v)
	}

	cards := []Card{
		Card{
			"EMAIL": []*Field{
				{Value: "me@example.org", Params: Params{"TYPE": {"home"}}},
				{Value: "me@example.com", Params: Params{"TYPE": {"work"}, "PREF": {"1"}}},
			},
		},
		Card{
			"EMAIL": []*Field{
				{Value: "me@example.org", Params: Params{"TYPE": {"home"}, "PREF": {"25"}}},
				{Value: "me@example.com", Params: Params{"TYPE": {"work"}, "PREF": {"50"}}},
			},
		},
		// v3.0
		Card{
			"EMAIL": []*Field{
				{Value: "me@example.org", Params: Params{"TYPE": {"home"}}},
				{Value: "me@example.com", Params: Params{"TYPE": {"work", "pref"}}},
			},
		},
	}

	for _, card := range cards {
		if pref := card.Preferred(FieldEmail); pref != card["EMAIL"][1] {
			t.Errorf("Expected card preferred email to be %+v but got %+v", card["EMAIL"][1], pref)
		}
		if v := card.PreferredValue(FieldEmail); v != "me@example.com" {
			t.Errorf("Expected card preferred email to be %q but got %q", "me@example.com", v)
		}
	}
}

func TestCard_Name(t *testing.T) {
	card := make(Card)
	if name := card.Name(); name != nil {
		t.Errorf("Expected empty card name to be %+v but got %+v", nil, name)
	}
	if names := card.Names(); names != nil {
		t.Errorf("Expected empty card names to be %+v but got %+v", nil, names)
	}

	expectedName := &Name{
		FamilyName: "Doe",
		GivenName:  "J.",
	}
	expectedNames := []*Name{expectedName}
	card.AddName(expectedName)
	if name := card.Name(); !reflect.DeepEqual(expectedName, name) {
		t.Errorf("Expected populated card name to be %+v but got %+v", expectedName, name)
	}
	if names := card.Names(); !reflect.DeepEqual(expectedNames, names) {
		t.Errorf("Expected populated card names to be %+v but got %+v", expectedNames, names)
	}
}

func TestCard_Kind(t *testing.T) {
	card := make(Card)

	if kind := card.Kind(); kind != KindIndividual {
		t.Errorf("Expected kind of empty card to be %q but got %q", KindIndividual, kind)
	}

	card.SetKind(KindOrganization)
	if kind := card.Kind(); kind != KindOrganization {
		t.Errorf("Expected kind of populated card to be %q but got %q", KindOrganization, kind)
	}
}

func TestCard_FormattedNames(t *testing.T) {
	card := make(Card)

	expectedNames := []*Field{{Value: ""}}
	if names := card.FormattedNames(); !reflect.DeepEqual(expectedNames, names) {
		t.Errorf("Expected empty card formatted names to be %+v but got %+v", expectedNames, names)
	}

	expectedNames = []*Field{{Value: "Akiyama Mio"}}
	card.SetValue(FieldFormattedName, expectedNames[0].Value)
	if names := card.FormattedNames(); !reflect.DeepEqual(expectedNames, names) {
		t.Errorf("Expected populated card formatted names to be %+v but got %+v", expectedNames, names)
	}
}

func TestCard_Gender(t *testing.T) {
	card := make(Card)

	var expectedSex Sex
	var expectedIdentity string
	if sex, identity := card.Gender(); sex != expectedSex || identity != expectedIdentity {
		t.Errorf("Expected gender to be (%q %q) but got (%q %q)", expectedSex, expectedIdentity, sex, identity)
	}

	expectedSex = SexFemale
	card.SetGender(expectedSex, expectedIdentity)
	if sex, identity := card.Gender(); sex != expectedSex || identity != expectedIdentity {
		t.Errorf("Expected gender to be (%q %q) but got (%q %q)", expectedSex, expectedIdentity, sex, identity)
	}

	expectedSex = SexOther
	expectedIdentity = "<3"
	card.SetGender(expectedSex, expectedIdentity)
	if sex, identity := card.Gender(); sex != expectedSex || identity != expectedIdentity {
		t.Errorf("Expected gender to be (%q %q) but got (%q %q)", expectedSex, expectedIdentity, sex, identity)
	}
}

func TestCard_Address(t *testing.T) {
	card := make(Card)

	if address := card.Address(); address != nil {
		t.Errorf("Expected empty card address to be nil, got %v", address)
	}
	if addresses := card.Addresses(); addresses != nil {
		t.Errorf("Expected empty card addresses to be nil, got %v", addresses)
	}

	added := &Address{
		StreetAddress: "1 Trafalgar Square",
		Locality:      "London",
		PostalCode:    "WC2N",
		Country:       "United Kingdom",
	}
	card.AddAddress(added)

	equal := func(a, b *Address) bool {
		if (a == nil && b != nil) || (b == nil && a != nil) {
			return false
		}
		a.Field, b.Field = nil, nil
		return reflect.DeepEqual(a, b)
	}

	if address := card.Address(); !equal(added, address) {
		t.Errorf("Expected address to be %+v but got %+v", added, address)
	}
	if addresses := card.Addresses(); len(addresses) != 1 || !equal(added, addresses[0]) {
		t.Errorf("Expected addresses to be %+v, got %+v", []*Address{added}, addresses)
	}
}

func TestCard_Revision(t *testing.T) {
	card := make(Card)

	if rev, err := card.Revision(); err != nil {
		t.Fatal("Expected no error when getting revision of an empty card, got:", err)
	} else if !rev.IsZero() {
		t.Error("Expected a zero time when getting revision of an empty card, got:", rev)
	}

	expected := time.Date(1984, time.November, 4, 0, 0, 0, 0, time.UTC)
	card.SetRevision(expected)
	if rev, err := card.Revision(); err != nil {
		t.Fatal("Expected no error when getting revision of a populated card, got:", err)
	} else if !rev.Equal(rev) {
		t.Error("Expected revision to be %v but got %v", expected, rev)
	}
}
<commit_msg>gofmt -s<commit_after>package vcard

import (
	"reflect"
	"testing"
	"time"
)

var testCard = Card{
	"VERSION": []*Field{{Value: "4.0"}},
	"UID":     []*Field{{Value: "urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1"}},
	"FN": []*Field{{
		Value:  "J. Doe",
		Params: Params{"PID": {"1.1"}},
	}},
	"N": []*Field{{Value: "Doe;J.;;;"}},
	"EMAIL": []*Field{{
		Value:  "jdoe@example.com",
		Params: Params{"PID": {"1.1"}},
	}},
	"CLIENTPIDMAP": []*Field{{Value: "1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556"}},
}

var testCardHandmade = Card{
	"VERSION": []*Field{{Value: "4.0"}},
	"N":       []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN":      []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value:  "me@joebloggs.com",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"TEL": []*Field{{
		Value:  "tel:+44 20 1234 5678",
		Params: Params{"TYPE": {"\"cell", "home\""}, "PREF": {"1"}},
	}},
	"ADR": []*Field{{
		Value:  ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"URL": []*Field{{
		Value:  "http://joebloggs.com",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"IMPP": []*Field{{
		Value:  "skype:joe.bloggs",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
	"X-SOCIALPROFILE": []*Field{{
		Value:  "twitter:https://twitter.com/joebloggs",
		Params: Params{"TYPE": {"home"}, "PREF": {"1"}},
	}},
}

var testCardGoogle = Card{
	"VERSION": []*Field{{Value: "3.0"}},
	"N":       []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN":      []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value:  "me@joebloggs.com",
		Params: Params{"TYPE": {"INTERNET", "HOME"}},
	}},
	"TEL": []*Field{{
		Value:  "+44 20 1234 5678",
		Params: Params{"TYPE": {"CELL"}},
	}},
	"ADR": []*Field{{
		Value:  ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: Params{"TYPE": {"HOME"}},
	}},
	"URL": []*Field{
		{Value: "http\\://joebloggs.com", Group: "item1"},
		{Value: "http\\://twitter.com/test", Group: "item2"},
	},
	"X-SKYPE": []*Field{{Value: "joe.bloggs"}},
	"X-ABLABEL": []*Field{
		{Value: "_$!<HomePage>!$_", Group: "item1"},
		{Value: "Twitter", Group: "item2"},
	},
}

var testCardApple = Card{
	"VERSION": []*Field{{Value: "3.0"}},
	"N":       []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN":      []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value:  "me@joebloggs.com",
		Params: Params{"TYPE": {"INTERNET", "HOME", "pref"}},
	}},
	"TEL": []*Field{{
		Value:  "+44 20 1234 5678",
		Params: Params{"TYPE": {"CELL", "VOICE", "pref"}},
	}},
	"ADR": []*Field{{
		Value:  ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: Params{"TYPE": {"HOME", "pref"}},
	}},
	"URL": []*Field{{
		Value:  "http://joebloggs.com",
		Params: Params{"TYPE": {"pref"}},
		Group:  "item1",
	}},
	"X-ABLABEL": []*Field{
		{Value: "_$!<HomePage>!$_", Group: "item1"},
	},
	"IMPP": []*Field{{
		Value:  "skype:joe.bloggs",
		Params: Params{"X-SERVICE-TYPE": {"Skype"}, "TYPE": {"HOME", "pref"}},
	}},
	"X-SOCIALPROFILE": []*Field{{
		Value:  "https://twitter.com/joebloggs",
		Params: Params{"TYPE": {"twitter"}},
	}},
}

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

	expected := []string{"a", "b", "c", "", ""}
	for i, exp := range expected {
		if v := maybeGet(l, i); v != exp {
			t.Errorf("maybeGet(l, %v): expected %q but got %q", i, exp, v)
		}
	}
}

func TestCard(t *testing.T) {
	testCardFullName := testCard["FN"][0]
	if field := testCard.Get(FieldFormattedName); testCardFullName != field {
		t.Errorf("Expected card FN field to be %+v but got %+v", testCardFullName, field)
	}
	if v := testCard.Value(FieldFormattedName); v != testCardFullName.Value {
		t.Errorf("Expected card FN field to be %q but got %q", testCardFullName.Value, v)
	}

	if field := testCard.Get("X-IDONTEXIST"); field != nil {
		t.Errorf("Expected card X-IDONTEXIST field to be %+v but got %+v", nil, field)
	}
	if v := testCard.Value("X-IDONTEXIST"); v != "" {
		t.Errorf("Expected card X-IDONTEXIST field value to be %q but got %q", "", v)
	}

	cardMultipleValues := Card{
		"EMAIL": []*Field{
			{Value: "me@example.org", Params: Params{"TYPE": {"home"}}},
			{Value: "me@example.com", Params: Params{"TYPE": {"work"}}},
		},
	}
	expected := []string{"me@example.org", "me@example.com"}
	if values := cardMultipleValues.Values(FieldEmail); !reflect.DeepEqual(expected, values) {
		t.Errorf("Expected card emails to be %+v but got %+v", expected, values)
	}
	if values := cardMultipleValues.Values("X-IDONTEXIST"); values != nil {
		t.Errorf("Expected card X-IDONTEXIST values to be %+v but got %+v", nil, values)
	}
}

func TestCard_AddValue(t *testing.T) {
	card := make(Card)

	name1 := "Akiyama Mio"
	card.AddValue("FN", name1)
	if values := card.Values("FN"); len(values) != 1 || values[0] != name1 {
		t.Errorf("Expected one FN value, got %v", values)
	}

	name2 := "Mio Akiyama"
	card.AddValue("FN", name2)
	if values := card.Values("FN"); len(values) != 2 || values[0] != name1 || values[1] != name2 {
		t.Errorf("Expected two FN values, got %v", values)
	}
}

func TestCard_Preferred(t *testing.T) {
	if pref := testCard.Preferred("X-IDONTEXIST"); pref != nil {
		t.Errorf("Expected card preferred X-IDONTEXIST field to be %+v but got %+v", nil, pref)
	}
	if v := testCard.PreferredValue("X-IDONTEXIST"); v != "" {
		t.Errorf("Expected card preferred X-IDONTEXIST field value to be %q but got %q", "", v)
	}

	cards := []Card{
		{
			"EMAIL": []*Field{
				{Value: "me@example.org", Params: Params{"TYPE": {"home"}}},
				{Value: "me@example.com", Params: Params{"TYPE": {"work"}, "PREF": {"1"}}},
			},
		},
		{
			"EMAIL": []*Field{
				{Value: "me@example.org", Params: Params{"TYPE": {"home"}, "PREF": {"25"}}},
				{Value: "me@example.com", Params: Params{"TYPE": {"work"}, "PREF": {"50"}}},
			},
		},
		// v3.0
		{
			"EMAIL": []*Field{
				{Value: "me@example.org", Params: Params{"TYPE": {"home"}}},
				{Value: "me@example.com", Params: Params{"TYPE": {"work", "pref"}}},
			},
		},
	}

	for _, card := range cards {
		if pref := card.Preferred(FieldEmail); pref != card["EMAIL"][1] {
			t.Errorf("Expected card preferred email to be %+v but got %+v", card["EMAIL"][1], pref)
		}
		if v := card.PreferredValue(FieldEmail); v != "me@example.com" {
			t.Errorf("Expected card preferred email to be %q but got %q", "me@example.com", v)
		}
	}
}

func TestCard_Name(t *testing.T) {
	card := make(Card)
	if name := card.Name(); name != nil {
		t.Errorf("Expected empty card name to be %+v but got %+v", nil, name)
	}
	if names := card.Names(); names != nil {
		t.Errorf("Expected empty card names to be %+v but got %+v", nil, names)
	}

	expectedName := &Name{
		FamilyName: "Doe",
		GivenName:  "J.",
	}
	expectedNames := []*Name{expectedName}
	card.AddName(expectedName)
	if name := card.Name(); !reflect.DeepEqual(expectedName, name) {
		t.Errorf("Expected populated card name to be %+v but got %+v", expectedName, name)
	}
	if names := card.Names(); !reflect.DeepEqual(expectedNames, names) {
		t.Errorf("Expected populated card names to be %+v but got %+v", expectedNames, names)
	}
}

func TestCard_Kind(t *testing.T) {
	card := make(Card)

	if kind := card.Kind(); kind != KindIndividual {
		t.Errorf("Expected kind of empty card to be %q but got %q", KindIndividual, kind)
	}

	card.SetKind(KindOrganization)
	if kind := card.Kind(); kind != KindOrganization {
		t.Errorf("Expected kind of populated card to be %q but got %q", KindOrganization, kind)
	}
}

func TestCard_FormattedNames(t *testing.T) {
	card := make(Card)

	expectedNames := []*Field{{Value: ""}}
	if names := card.FormattedNames(); !reflect.DeepEqual(expectedNames, names) {
		t.Errorf("Expected empty card formatted names to be %+v but got %+v", expectedNames, names)
	}

	expectedNames = []*Field{{Value: "Akiyama Mio"}}
	card.SetValue(FieldFormattedName, expectedNames[0].Value)
	if names := card.FormattedNames(); !reflect.DeepEqual(expectedNames, names) {
		t.Errorf("Expected populated card formatted names to be %+v but got %+v", expectedNames, names)
	}
}

func TestCard_Gender(t *testing.T) {
	card := make(Card)

	var expectedSex Sex
	var expectedIdentity string
	if sex, identity := card.Gender(); sex != expectedSex || identity != expectedIdentity {
		t.Errorf("Expected gender to be (%q %q) but got (%q %q)", expectedSex, expectedIdentity, sex, identity)
	}

	expectedSex = SexFemale
	card.SetGender(expectedSex, expectedIdentity)
	if sex, identity := card.Gender(); sex != expectedSex || identity != expectedIdentity {
		t.Errorf("Expected gender to be (%q %q) but got (%q %q)", expectedSex, expectedIdentity, sex, identity)
	}

	expectedSex = SexOther
	expectedIdentity = "<3"
	card.SetGender(expectedSex, expectedIdentity)
	if sex, identity := card.Gender(); sex != expectedSex || identity != expectedIdentity {
		t.Errorf("Expected gender to be (%q %q) but got (%q %q)", expectedSex, expectedIdentity, sex, identity)
	}
}

func TestCard_Address(t *testing.T) {
	card := make(Card)

	if address := card.Address(); address != nil {
		t.Errorf("Expected empty card address to be nil, got %v", address)
	}
	if addresses := card.Addresses(); addresses != nil {
		t.Errorf("Expected empty card addresses to be nil, got %v", addresses)
	}

	added := &Address{
		StreetAddress: "1 Trafalgar Square",
		Locality:      "London",
		PostalCode:    "WC2N",
		Country:       "United Kingdom",
	}
	card.AddAddress(added)

	equal := func(a, b *Address) bool {
		if (a == nil && b != nil) || (b == nil && a != nil) {
			return false
		}
		a.Field, b.Field = nil, nil
		return reflect.DeepEqual(a, b)
	}

	if address := card.Address(); !equal(added, address) {
		t.Errorf("Expected address to be %+v but got %+v", added, address)
	}
	if addresses := card.Addresses(); len(addresses) != 1 || !equal(added, addresses[0]) {
		t.Errorf("Expected addresses to be %+v, got %+v", []*Address{added}, addresses)
	}
}

func TestCard_Revision(t *testing.T) {
	card := make(Card)

	if rev, err := card.Revision(); err != nil {
		t.Fatal("Expected no error when getting revision of an empty card, got:", err)
	} else if !rev.IsZero() {
		t.Error("Expected a zero time when getting revision of an empty card, got:", rev)
	}

	expected := time.Date(1984, time.November, 4, 0, 0, 0, 0, time.UTC)
	card.SetRevision(expected)
	if rev, err := card.Revision(); err != nil {
		t.Fatal("Expected no error when getting revision of a populated card, got:", err)
	} else if !rev.Equal(rev) {
		t.Error("Expected revision to be %v but got %v", expected, rev)
	}
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2019 gRPC authors.
 *
 * 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 grpcgcp

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp/grpc_gcp"
	"google.golang.org/grpc/balancer"
)

func newGCPPicker(readySCRefs []*subConnRef, gb *gcpBalancer) balancer.Picker {
	return &gcpPicker{
		gcpBalancer: gb,
		scRefs:      readySCRefs,
		// The pool config is unavailable until Pick is called with a config in the context.
		poolCfg: nil,
	}
}

type gcpPicker struct {
	gcpBalancer *gcpBalancer
	mu          sync.Mutex
	scRefs      []*subConnRef
	poolCfg     *poolConfig
}

func (p *gcpPicker) initializePoolCfg(poolCfg *poolConfig) {
	if p.poolCfg == nil && poolCfg != nil {
		p.poolCfg = poolCfg
		p.gcpBalancer.enforceMinSize(int(poolCfg.minConn))
	}
}

func (p *gcpPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
	if len(p.scRefs) <= 0 {
		return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
	}

	ctx := info.Ctx
	gcpCtx, hasGcpCtx := ctx.Value(gcpKey).(*gcpContext)
	boundKey := ""

	if hasGcpCtx {
		if p.poolCfg == nil && gcpCtx.poolCfg != nil {
			p.initializePoolCfg(gcpCtx.poolCfg)
		}
		affinity := gcpCtx.affinityCfg
		if affinity != nil {
			locator := affinity.GetAffinityKey()
			cmd := affinity.GetCommand()
			if cmd == grpc_gcp.AffinityConfig_BOUND || cmd == grpc_gcp.AffinityConfig_UNBIND {
				a, err := getAffinityKeyFromMessage(locator, gcpCtx.reqMsg)
				if err != nil {
					return balancer.PickResult{}, fmt.Errorf(
						"failed to retrieve affinity key from request message: %v", err)
				}
				boundKey = a
			}
		}
	}

	scRef, err := p.getSubConnRef(boundKey)
	if err != nil {
		return balancer.PickResult{}, err
	}
	if scRef == nil {
		return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
	}
	scRef.streamsIncr()

	// define callback for post process once call is done
	callback := func(info balancer.DoneInfo) {
		if info.Err == nil {
			if hasGcpCtx {
				affinity := gcpCtx.affinityCfg
				locator := affinity.GetAffinityKey()
				cmd := affinity.GetCommand()
				if cmd == grpc_gcp.AffinityConfig_BIND {
					bindKey, err := getAffinityKeyFromMessage(locator, gcpCtx.replyMsg)
					if err == nil {
						p.gcpBalancer.bindSubConn(bindKey, scRef.subConn)
					}
				} else if cmd == grpc_gcp.AffinityConfig_UNBIND {
					p.gcpBalancer.unbindSubConn(boundKey)
				}
			}
		}
		scRef.streamsDecr()
	}
	return balancer.PickResult{SubConn: scRef.subConn, Done: callback}, nil
}

// getSubConnRef returns the subConnRef object that contains the subconn
// ready to be used by picker.
func (p *gcpPicker) getSubConnRef(boundKey string) (*subConnRef, error) {
	if boundKey != "" {
		if ref, ok := p.gcpBalancer.getReadySubConnRef(boundKey); ok {
			return ref, nil
		}
	}

	minScRef := p.scRefs[0]
	minStreamsCnt := minScRef.getStreamsCnt()
	for _, scRef := range p.scRefs {
		if scRef.getStreamsCnt() < minStreamsCnt {
			minStreamsCnt = scRef.getStreamsCnt()
			minScRef = scRef
		}
	}

	// If the least busy connection still has capacity, use it
	if minStreamsCnt < int32(p.poolCfg.maxStream) {
		return minScRef, nil
	}

	if p.poolCfg.maxConn == 0 || p.gcpBalancer.getConnectionPoolSize() < int(p.poolCfg.maxConn) {
		// Ask balancer to create new subconn when all current subconns are busy and
		// the connection pool still has capacity (either unlimited or maxSize is not reached).
		p.gcpBalancer.newSubConn()

		// Let this picker return ErrNoSubConnAvailable because it needs some time
		// for the subconn to be READY.
		return nil, balancer.ErrNoSubConnAvailable
	}

	// If no capacity for the pool size and every connection reachs the soft limit,
	// Then picks the least busy one anyway.
	return minScRef, nil
}

// getAffinityKeyFromMessage retrieves the affinity key from proto message using
// the key locator defined in the affinity config.
func getAffinityKeyFromMessage(
	locator string,
	msg interface{},
) (affinityKey string, err error) {
	names := strings.Split(locator, ".")
	if len(names) == 0 {
		return "", fmt.Errorf("Empty affinityKey locator")
	}

	if msg == nil {
		return "", fmt.Errorf("cannot get string value from nil message")
	}
	val := reflect.ValueOf(msg).Elem()

	// Fields in names except for the last one.
	for _, name := range names[:len(names)-1] {
		valField := val.FieldByName(strings.Title(name))
		if valField.Kind() != reflect.Ptr && valField.Kind() != reflect.Struct {
			return "", fmt.Errorf("Invalid locator path for %v", locator)
		}
		val = valField.Elem()
	}

	valField := val.FieldByName(strings.Title(names[len(names)-1]))
	if valField.Kind() != reflect.String {
		return "", fmt.Errorf("Cannot get string value from %v", locator)
	}
	return valField.String(), nil
}

// NewErrPicker returns a picker that always returns err on Pick().
func newErrPicker(err error) balancer.Picker {
	return &errPicker{err: err}
}

type errPicker struct {
	err error // Pick() always returns this err.
}

func (p *errPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
	return balancer.PickResult{}, p.err
}
<commit_msg>Remove unused mutex from gcp_picker<commit_after>/*
 *
 * Copyright 2019 gRPC authors.
 *
 * 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 grpcgcp

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp/grpc_gcp"
	"google.golang.org/grpc/balancer"
)

func newGCPPicker(readySCRefs []*subConnRef, gb *gcpBalancer) balancer.Picker {
	return &gcpPicker{
		gcpBalancer: gb,
		scRefs:      readySCRefs,
		// The pool config is unavailable until Pick is called with a config in the context.
		poolCfg: nil,
	}
}

type gcpPicker struct {
	gcpBalancer *gcpBalancer
	scRefs      []*subConnRef
	poolCfg     *poolConfig
}

func (p *gcpPicker) initializePoolCfg(poolCfg *poolConfig) {
	if p.poolCfg == nil && poolCfg != nil {
		p.poolCfg = poolCfg
		p.gcpBalancer.enforceMinSize(int(poolCfg.minConn))
	}
}

func (p *gcpPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
	if len(p.scRefs) <= 0 {
		return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
	}

	ctx := info.Ctx
	gcpCtx, hasGcpCtx := ctx.Value(gcpKey).(*gcpContext)
	boundKey := ""

	if hasGcpCtx {
		if p.poolCfg == nil && gcpCtx.poolCfg != nil {
			p.initializePoolCfg(gcpCtx.poolCfg)
		}
		affinity := gcpCtx.affinityCfg
		if affinity != nil {
			locator := affinity.GetAffinityKey()
			cmd := affinity.GetCommand()
			if cmd == grpc_gcp.AffinityConfig_BOUND || cmd == grpc_gcp.AffinityConfig_UNBIND {
				a, err := getAffinityKeyFromMessage(locator, gcpCtx.reqMsg)
				if err != nil {
					return balancer.PickResult{}, fmt.Errorf(
						"failed to retrieve affinity key from request message: %v", err)
				}
				boundKey = a
			}
		}
	}

	scRef, err := p.getSubConnRef(boundKey)
	if err != nil {
		return balancer.PickResult{}, err
	}
	if scRef == nil {
		return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
	}
	scRef.streamsIncr()

	// define callback for post process once call is done
	callback := func(info balancer.DoneInfo) {
		if info.Err == nil {
			if hasGcpCtx {
				affinity := gcpCtx.affinityCfg
				locator := affinity.GetAffinityKey()
				cmd := affinity.GetCommand()
				if cmd == grpc_gcp.AffinityConfig_BIND {
					bindKey, err := getAffinityKeyFromMessage(locator, gcpCtx.replyMsg)
					if err == nil {
						p.gcpBalancer.bindSubConn(bindKey, scRef.subConn)
					}
				} else if cmd == grpc_gcp.AffinityConfig_UNBIND {
					p.gcpBalancer.unbindSubConn(boundKey)
				}
			}
		}
		scRef.streamsDecr()
	}
	return balancer.PickResult{SubConn: scRef.subConn, Done: callback}, nil
}

// getSubConnRef returns the subConnRef object that contains the subconn
// ready to be used by picker.
func (p *gcpPicker) getSubConnRef(boundKey string) (*subConnRef, error) {
	if boundKey != "" {
		if ref, ok := p.gcpBalancer.getReadySubConnRef(boundKey); ok {
			return ref, nil
		}
	}

	minScRef := p.scRefs[0]
	minStreamsCnt := minScRef.getStreamsCnt()
	for _, scRef := range p.scRefs {
		if scRef.getStreamsCnt() < minStreamsCnt {
			minStreamsCnt = scRef.getStreamsCnt()
			minScRef = scRef
		}
	}

	// If the least busy connection still has capacity, use it
	if minStreamsCnt < int32(p.poolCfg.maxStream) {
		return minScRef, nil
	}

	if p.poolCfg.maxConn == 0 || p.gcpBalancer.getConnectionPoolSize() < int(p.poolCfg.maxConn) {
		// Ask balancer to create new subconn when all current subconns are busy and
		// the connection pool still has capacity (either unlimited or maxSize is not reached).
		p.gcpBalancer.newSubConn()

		// Let this picker return ErrNoSubConnAvailable because it needs some time
		// for the subconn to be READY.
		return nil, balancer.ErrNoSubConnAvailable
	}

	// If no capacity for the pool size and every connection reachs the soft limit,
	// Then picks the least busy one anyway.
	return minScRef, nil
}

// getAffinityKeyFromMessage retrieves the affinity key from proto message using
// the key locator defined in the affinity config.
func getAffinityKeyFromMessage(
	locator string,
	msg interface{},
) (affinityKey string, err error) {
	names := strings.Split(locator, ".")
	if len(names) == 0 {
		return "", fmt.Errorf("Empty affinityKey locator")
	}

	if msg == nil {
		return "", fmt.Errorf("cannot get string value from nil message")
	}
	val := reflect.ValueOf(msg).Elem()

	// Fields in names except for the last one.
	for _, name := range names[:len(names)-1] {
		valField := val.FieldByName(strings.Title(name))
		if valField.Kind() != reflect.Ptr && valField.Kind() != reflect.Struct {
			return "", fmt.Errorf("Invalid locator path for %v", locator)
		}
		val = valField.Elem()
	}

	valField := val.FieldByName(strings.Title(names[len(names)-1]))
	if valField.Kind() != reflect.String {
		return "", fmt.Errorf("Cannot get string value from %v", locator)
	}
	return valField.String(), nil
}

// NewErrPicker returns a picker that always returns err on Pick().
func newErrPicker(err error) balancer.Picker {
	return &errPicker{err: err}
}

type errPicker struct {
	err error // Pick() always returns this err.
}

func (p *errPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
	return balancer.PickResult{}, p.err
}
<|endoftext|>


<commit_before>package harvest

import (
	"crypto/md5"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
)

func mockResponse(paths ...string) *httptest.Server {
	parts := []string{".", "testdata"}
	filename := filepath.Join(append(parts, paths...)...)

	mockData, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.Write(mockData)
	}))
}

func mockDynamicPathResponse() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {

		// Build the path for the dynamic content
		parts := []string{".", "testdata"}
		parts = append(parts, strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/")...)
		// Remove security strings
		queryStringPart := r.URL.RawQuery
		if queryStringPart != "" {
			parts[len(parts)-1] = fmt.Sprintf("%s-%x", parts[len(parts)-1], md5.Sum([]byte(queryStringPart)))
		}
		parts[len(parts)-1] = parts[len(parts)-1] + ".json"
		filename := filepath.Join(parts...)

		if _, err := os.Stat(filename); os.IsNotExist(err) {
			http.Error(rw, fmt.Sprintf("%s doesn't exist. Create it with the mock you'd like to use.\n Args were: %s", filename, queryStringPart), http.StatusNotFound)
			return
		}

		mockData, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Fatal(err)
		}
		rw.Write(mockData)

	}))
}

func mockErrorResponse(code int) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		http.Error(rw, "An error occurred", code)
	}))
}
<commit_msg>Adds mockRedirectResponse(), which responds to PUT and POST requests with a redirect header, and GET requests with the identified stub file. This aids testing of CreateN() and UpdateN() methods.<commit_after>package harvest

import (
	"crypto/md5"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
)

func mockResponse(paths ...string) *httptest.Server {
	parts := []string{".", "testdata"}
	filename := filepath.Join(append(parts, paths...)...)

	mockData, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.Write(mockData)
	}))
}

func mockRedirectResponse(paths ...string) *httptest.Server {
	parts := []string{".", "testdata"}
	filename := filepath.Join(append(parts, paths...)...)

	mockData, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" || r.Method == "PUT" {
			rw.Header().Set("Location", "/redirect/123456")
			rw.Write([]byte{})
		} else {
			rw.Write(mockData)
		}
	}))
}

func mockDynamicPathResponse() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {

		// Build the path for the dynamic content
		parts := []string{".", "testdata"}
		parts = append(parts, strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/")...)
		// Remove security strings
		queryStringPart := r.URL.RawQuery
		if queryStringPart != "" {
			parts[len(parts)-1] = fmt.Sprintf("%s-%x", parts[len(parts)-1], md5.Sum([]byte(queryStringPart)))
		}
		parts[len(parts)-1] = parts[len(parts)-1] + ".json"
		filename := filepath.Join(parts...)

		if _, err := os.Stat(filename); os.IsNotExist(err) {
			http.Error(rw, fmt.Sprintf("%s doesn't exist. Create it with the mock you'd like to use.\n Args were: %s", filename, queryStringPart), http.StatusNotFound)
			return
		}

		mockData, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Fatal(err)
		}
		rw.Write(mockData)

	}))
}

func mockErrorResponse(code int) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		http.Error(rw, "An error occurred", code)
	}))
}
<|endoftext|>


<commit_before>package mocks

import (
	"github.com/aleasoluciones/simpleamqp"
	"github.com/stretchr/testify/mock"
)

import "time"

type AMQPConsumer struct {
	mock.Mock
}

func (m *AMQPConsumer) Receive(exchange string, routingKeys []string, queue string, options simpleamqp.QueueOptions, queueTimeout time.Duration) chan simpleamqp.AmqpMessage {
	ret := m.Called(exchange, routingKeys, queue, queueTimeout)
	return ret.Get(0).(chan simpleamqp.AmqpMessage)
}
<commit_msg>Update AMQPConsumer mock<commit_after>package mocks

import "github.com/aleasoluciones/simpleamqp"
import "github.com/stretchr/testify/mock"

import "time"

type AMQPConsumer struct {
	mock.Mock
}

func (_m *AMQPConsumer) Receive(exchange string, routingKeys []string, queue string, queueOptions simpleamqp.QueueOptions, queueTimeout time.Duration) chan simpleamqp.AmqpMessage {
	ret := _m.Called(exchange, routingKeys, queue, queueOptions, queueTimeout)

	var r0 chan simpleamqp.AmqpMessage
	if rf, ok := ret.Get(0).(func(string, []string, string, simpleamqp.QueueOptions, time.Duration) chan simpleamqp.AmqpMessage); ok {
		r0 = rf(exchange, routingKeys, queue, queueOptions, queueTimeout)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(chan simpleamqp.AmqpMessage)
		}
	}

	return r0
}
func (_m *AMQPConsumer) ReceiveWithoutTimeout(exchange string, routingKeys []string, queue string, queueOptions simpleamqp.QueueOptions) chan simpleamqp.AmqpMessage {
	ret := _m.Called(exchange, routingKeys, queue, queueOptions)

	var r0 chan simpleamqp.AmqpMessage
	if rf, ok := ret.Get(0).(func(string, []string, string, simpleamqp.QueueOptions) chan simpleamqp.AmqpMessage); ok {
		r0 = rf(exchange, routingKeys, queue, queueOptions)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(chan simpleamqp.AmqpMessage)
		}
	}

	return r0
}
<|endoftext|>


<commit_before>package components

import (
	"github.com/bep/gr"
	"github.com/bep/gr/attr"
	"github.com/bep/gr/el"
	"github.com/bep/grouter"
)

type Layout struct {
	*gr.This
	Pages
	Brand       string
	ActivePage  string
	ApiEndpoint string
	Content     *gr.ReactComponent
}

// Implements the Renderer interface.
func (l Layout) Render() gr.Component {
	return el.Div(
		gr.CSS("main-wrapper"),

		// Nav
		gr.New(&Nav{Brand: l.Brand, Pages: l.Pages}).CreateElement(l.This.Props()),

		//Content
		gr.New(&Content{}).CreateElement(gr.Props{"Title": l.ActivePage, "ApiEndpoint": l.Pages[l.ActivePage].ApiEndpoint}),
	)
}

func (l Layout) createLinkListItem(path, Title string) gr.Modifier {
	return el.ListItem(
		grouter.MarkIfActive(l.Props(), path),
		attr.Role("presentation"),
		grouter.Link(path, Title))
}

func (l Layout) onClick(event *gr.Event) {
	l.SetState(gr.State{"counter": l.State().Int("counter") + 1})
}

// Implements the ShouldComponentUpdate interface.
func (l Layout) ShouldComponentUpdate(
	next gr.Cops) bool {

	return l.State().HasChanged(next.State, "counter")
}
<commit_msg>cleanup<commit_after>package components

import (
	"github.com/bep/gr"
	"github.com/bep/gr/el"
)

type Layout struct {
	*gr.This
	Pages
	Brand       string
	ActivePage  string
	ApiEndpoint string
	Content     *gr.ReactComponent
}

// Implements the Renderer interface.
func (l Layout) Render() gr.Component {
	return el.Div(
		gr.CSS("main-wrapper"),

		// Nav
		gr.New(&Nav{Brand: l.Brand, Pages: l.Pages}).CreateElement(l.This.Props()),

		//Content
		gr.New(&Content{}).CreateElement(gr.Props{"Title": l.ActivePage, "ApiEndpoint": l.Pages[l.ActivePage].ApiEndpoint}),
	)
}
<|endoftext|>