<commit_before>package expirecache

import (
	"math/rand"
	"sync"
	"time"
)

type element struct {
	validUntil time.Time
	data       interface{}
	size       uint64
}

type Cache struct {
	sync.RWMutex
	cache     map[string]element
	keys      []string
	totalSize uint64
	maxSize   uint64
}

func New(maxSize uint64) *Cache {
	return &Cache{
		cache:   make(map[string]element),
		maxSize: maxSize,
	}
}

func (ec *Cache) Size() uint64 {
	ec.RLock()
	s := ec.totalSize
	ec.RUnlock()
	return s
}

func (ec *Cache) Items() int {
	ec.RLock()
	k := len(ec.keys)
	ec.RUnlock()
	return k
}

func (ec *Cache) Get(k string) (interface{}, bool) {
	ec.RLock()
	v, ok := ec.cache[k]
	ec.RUnlock()
	if !ok || v.validUntil.Before(timeNow()) {
		// Can't actually delete this element from the cache here since
		// we can't remove the key from ec.keys without a linear search.
		// It'll get removed during the next cleanup
		return nil, false
	}
	return v.data, ok
}

func (ec *Cache) Set(k string, v interface{}, size uint64, expire int32) {
	ec.Lock()
	oldv, ok := ec.cache[k]
	if !ok {
		ec.keys = append(ec.keys, k)
	} else {
		ec.totalSize -= oldv.size
	}

	ec.totalSize += size
	ec.cache[k] = element{validUntil: timeNow().Add(time.Duration(expire) * time.Second), data: v, size: size}

	for ec.maxSize > 0 && ec.totalSize > ec.maxSize {
		ec.randomEvict()
	}

	ec.Unlock()
}

func (ec *Cache) randomEvict() {
	slot := rand.Intn(len(ec.keys))
	k := ec.keys[slot]

	ec.keys[slot] = ec.keys[len(ec.keys)-1]
	ec.keys = ec.keys[:len(ec.keys)-1]

	v := ec.cache[k]
	ec.totalSize -= v.size

	delete(ec.cache, k)
}

func (ec *Cache) Cleaner(d time.Duration) {

	for {
		cleanerSleep(d)

		now := timeNow()
		ec.Lock()

		// We could potentially be holding this lock for a long time,
		// but since we keep the cache expiration times small, we
		// expect only a small number of elements here to loop over

		for i := 0; i < len(ec.keys); i++ {
			k := ec.keys[i]
			v := ec.cache[k]
			if v.validUntil.Before(now) {
				ec.totalSize -= v.size
				delete(ec.cache, k)

				ec.keys[i] = ec.keys[len(ec.keys)-1]
				ec.keys = ec.keys[:len(ec.keys)-1]
				i-- // so we reprocess this index
			}
		}

		ec.Unlock()
		cleanerDone()
	}
}

var (
	timeNow      = time.Now
	cleanerSleep = time.Sleep
	cleanerDone  = func() {}
)
<commit_msg>add ApproximateCleaner()<commit_after>package expirecache

import (
	"math/rand"
	"sync"
	"time"
)

type element struct {
	validUntil time.Time
	data       interface{}
	size       uint64
}

type Cache struct {
	sync.RWMutex
	cache     map[string]element
	keys      []string
	totalSize uint64
	maxSize   uint64
}

func New(maxSize uint64) *Cache {
	return &Cache{
		cache:   make(map[string]element),
		maxSize: maxSize,
	}
}

func (ec *Cache) Size() uint64 {
	ec.RLock()
	s := ec.totalSize
	ec.RUnlock()
	return s
}

func (ec *Cache) Items() int {
	ec.RLock()
	k := len(ec.keys)
	ec.RUnlock()
	return k
}

func (ec *Cache) Get(k string) (interface{}, bool) {
	ec.RLock()
	v, ok := ec.cache[k]
	ec.RUnlock()
	if !ok || v.validUntil.Before(timeNow()) {
		// Can't actually delete this element from the cache here since
		// we can't remove the key from ec.keys without a linear search.
		// It'll get removed during the next cleanup
		return nil, false
	}
	return v.data, ok
}

func (ec *Cache) Set(k string, v interface{}, size uint64, expire int32) {
	ec.Lock()
	oldv, ok := ec.cache[k]
	if !ok {
		ec.keys = append(ec.keys, k)
	} else {
		ec.totalSize -= oldv.size
	}

	ec.totalSize += size
	ec.cache[k] = element{validUntil: timeNow().Add(time.Duration(expire) * time.Second), data: v, size: size}

	for ec.maxSize > 0 && ec.totalSize > ec.maxSize {
		ec.randomEvict()
	}

	ec.Unlock()
}

func (ec *Cache) randomEvict() {
	slot := rand.Intn(len(ec.keys))
	k := ec.keys[slot]

	ec.keys[slot] = ec.keys[len(ec.keys)-1]
	ec.keys = ec.keys[:len(ec.keys)-1]

	v := ec.cache[k]
	ec.totalSize -= v.size

	delete(ec.cache, k)
}

func (ec *Cache) Cleaner(d time.Duration) {

	for {
		cleanerSleep(d)

		now := timeNow()
		ec.Lock()

		// We could potentially be holding this lock for a long time,
		// but since we keep the cache expiration times small, we
		// expect only a small number of elements here to loop over

		for i := 0; i < len(ec.keys); i++ {
			k := ec.keys[i]
			v := ec.cache[k]
			if v.validUntil.Before(now) {
				ec.totalSize -= v.size
				delete(ec.cache, k)

				ec.keys[i] = ec.keys[len(ec.keys)-1]
				ec.keys = ec.keys[:len(ec.keys)-1]
				i-- // so we reprocess this index
			}
		}

		ec.Unlock()
		cleanerDone()
	}
}

func (ec *Cache) ApproximateCleaner(d time.Duration) {

	// every iteration, sample and clean this many items
	const sampleSize = 20
	// if we cleaned at least this many, run the loop again
	const rerunCount = 5

	for {
		cleanerSleep(d)

		now := timeNow()

		// probabilistic expiration algorithm from redis
		for {
			var cleaned int
			// by doing short iterations and releasing the lock in between, we don't block other requests from progressing.
			ec.Lock()
			for i := 0; len(ec.keys) > 0 && i < sampleSize; i++ {
				idx := rand.Intn(len(ec.keys))
				k := ec.keys[idx]
				v := ec.cache[k]
				if v.validUntil.Before(now) {
					ec.totalSize -= v.size
					delete(ec.cache, k)

					ec.keys[idx] = ec.keys[len(ec.keys)-1]
					ec.keys = ec.keys[:len(ec.keys)-1]
					cleaned++
				}
			}
			ec.Unlock()
			if cleaned < rerunCount {
				// "clean enough"
				break
			}
		}

		cleanerDone()
	}
}

var (
	timeNow      = time.Now
	cleanerSleep = time.Sleep
	cleanerDone  = func() {}
)
<|endoftext|>


<commit_before>package taskgraph

import "golang.org/x/net/context"

// Task is a logic repersentation of a computing unit.
// Each task contain at least one Node.
// Each task has exact one master Node and might have multiple salve Nodes.

// All event handler functions and should be non-blocking.
type Task interface {
	// This is useful to bring the task up to speed from scratch or if it recovers.
	Init(taskID uint64, framework Framework)

	// Task is finished up for exit. Last chance to save some task specific work.
	Exit()

	// Framework tells user task what current epoch is.
	// This give the task an opportunity to cleanup and regroup.
	SetEpoch(ctx context.Context, epoch uint64)

	// The meta/data notifications obey exactly-once semantics. Note that the same
	// meta string will be notified only once even if you flag the meta more than once.
	// ParentMetaReady(ctx Context, parentID uint64, meta string)
	// ChildMetaReady(ctx Context, childID uint64, meta string)

	// This now allow use to use arbirary type instead of Parents/Children.
	MetaReady(ctx context.Context, childID uint64, linkType, meta string)

	// These two should go away, folding into DataRequest.
	ParentDataReady(ctx context.Context, parentID uint64, req string, resp []byte)
	ChildDataReady(ctx context.Context, childID uint64, req string, resp []byte)

	// These are payload for application purpose.
	ServeAsParent(fromID uint64, req string) ([]byte, error)
	ServeAsChild(fromID uint64, req string) ([]byte, error)
}

type UpdateLog interface {
	UpdateID()
}

// Backupable is an interface that task need to implement if they want to have
// hot standby copy. This is another can of beans.
type Backupable interface {
	// Some hooks that need for master slave etc.
	BecamePrimary()
	BecameBackup()

	// Framework notify this copy to update. This should be the only way that
	// one update the state of copy.
	Update(log UpdateLog)
}
<commit_msg>remove traces of XMetaReady<commit_after>package taskgraph

import "golang.org/x/net/context"

// Task is a logic repersentation of a computing unit.
// Each task contain at least one Node.
// Each task has exact one master Node and might have multiple salve Nodes.

// All event handler functions and should be non-blocking.
type Task interface {
	// This is useful to bring the task up to speed from scratch or if it recovers.
	Init(taskID uint64, framework Framework)

	// Task is finished up for exit. Last chance to save some task specific work.
	Exit()

	// Framework tells user task what current epoch is.
	// This give the task an opportunity to cleanup and regroup.
	SetEpoch(ctx context.Context, epoch uint64)

	// The meta/data notifications obey exactly-once semantics. Note that the same
	// meta string will be notified only once even if you flag the meta more than once.
	// TODO: one can also get this from channel.
	MetaReady(ctx context.Context, childID uint64, linkType, meta string)

	// These two should go away, folding into DataRequest.
	ParentDataReady(ctx context.Context, parentID uint64, req string, resp []byte)
	ChildDataReady(ctx context.Context, childID uint64, req string, resp []byte)

	// These are payload for application purpose.
	ServeAsParent(fromID uint64, req string) ([]byte, error)
	ServeAsChild(fromID uint64, req string) ([]byte, error)
}

type UpdateLog interface {
	UpdateID()
}

// Backupable is an interface that task need to implement if they want to have
// hot standby copy. This is another can of beans.
type Backupable interface {
	// Some hooks that need for master slave etc.
	BecamePrimary()
	BecameBackup()

	// Framework notify this copy to update. This should be the only way that
	// one update the state of copy.
	Update(log UpdateLog)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/codegangsta/cli"
	"github.com/golang/glog"
	"github.com/rs/xhandler"
	"golang.org/x/net/context"

	"github.com/crackcomm/renderer/compiler"
	"github.com/crackcomm/renderer/renderweb"
	"github.com/crackcomm/renderer/storage"
	"github.com/crackcomm/renderer/watcher"

	// Profiler
	_ "net/http/pprof"
)

// Commands - List of renderer commands.
var Commands = []cli.Command{
	Web,
}

// Web - Web command.
var Web = cli.Command{
	Name:  "web",
	Usage: "starts renderer web API",
	Flags: []cli.Flag{
		// Compiler options
		cli.StringSliceFlag{
			Name:  "routes",
			Usage: "file containing routes in yaml format",
		},
		cli.StringFlag{
			Name:  "components",
			Usage: "directory containing components",
		},
		cli.BoolFlag{
			Name:  "watch",
			Usage: "watch for changes in components",
		},
		cli.BoolFlag{
			Name:  "compress",
			Usage: "removes repeated whitespaces",
		},
		cli.DurationFlag{
			Name:  "cache-expiration",
			Usage: "cache expiration time",
			Value: 15 * time.Minute,
		},
		cli.DurationFlag{
			Name:  "cache-cleanup",
			Usage: "cache cleanup interval",
			Value: 5 * time.Minute,
		},

		// Web server options
		cli.StringFlag{
			Name:  "listen-addr",
			Usage: "web interface listening address",
			Value: "127.0.0.1:6660",
		},
		cli.DurationFlag{
			Name:  "render-timeout",
			Usage: "component render timeout",
			Value: 5 * time.Second,
		},

		// HTTP server flags
		cli.DurationFlag{
			Name:   "http-read-timeout",
			EnvVar: "HTTP_READ_TIMEOUT",
			Usage:  "http server read timeout",
			Value:  time.Minute,
		},
		cli.DurationFlag{
			Name:   "http-write-timeout",
			EnvVar: "HTTP_WRITE_TIMEOUT",
			Usage:  "http server write timeout",
			Value:  time.Minute,
		},

		// Profiler
		cli.StringFlag{
			Name:  "pprof-addr",
			Usage: "pprof listening address",
		},
	},
	Action: func(c *cli.Context) {
		// Get components directory from --components flag
		// Print fatal error if not set
		if c.String("components") == "" {
			glog.Fatal("--components flag cannot be empty")
		}

		// Create a new storage in directory from --components flag
		storage, err := storage.New(
			storage.WithDir(c.String("components")),
			storage.WithCacheExpiration(c.Duration("cache-expiration")),
			storage.WithCacheCleanupInterval(c.Duration("cache-cleanup")),
			storage.WithWhitespaceRemoval(c.Bool("compress")),
		)
		if err != nil {
			glog.Fatalf("[storage] %v", err)
		}
		defer storage.Close()

		// Create a compiler from storage
		comp := compiler.New(storage)

		// Create a context with compiler
		ctx := compiler.NewContext(context.Background(), comp)

		// Turn routes into HTTP handler
		api, err := constructHandler(c.StringSlice("routes")...)
		if err != nil {
			glog.Fatalf("[routes] %v", err)
		}

		// Construct API handler
		handler := &atomicHandler{
			Context:  ctx,
			Current:  xhandler.New(ctx, api),
			Watching: c.Bool("watch"),
			Routes:   c.StringSlice("routes"),
			Mutex:    new(sync.RWMutex),
		}

		if c.Bool("watch") {
			// Start watching for changes in components directory
			var w *watcher.Watcher
			w, err = watcher.Start(c.String("components"), storage)
			if err != nil {
				glog.Fatal(err)
			}
			defer w.Stop()

			// Start watching for changes in routes
			for _, filename := range c.StringSlice("routes") {
				var watch *watcher.Watcher
				watch, err = watcher.Start(filename, handler)
				if err != nil {
					glog.Fatal(err)
				}
				defer watch.Stop()
			}
		}

		// Start profiler if enabled
		if pprofaddr := c.String("pprof-addr"); pprofaddr != "" {
			go func() {
				glog.Infof("[pprof] starting listener on %s", pprofaddr)
				if err := http.ListenAndServe(pprofaddr, nil); err != nil {
					glog.Fatal(err)
				}
			}()
		}

		// Construct http server
		server := &http.Server{
			Addr:           c.String("listen-addr"),
			Handler:        handler,
			ReadTimeout:    c.Duration("http-read-timeout"),
			WriteTimeout:   c.Duration("http-write-timeout"),
			MaxHeaderBytes: 64 * 1024,
		}

		glog.Infof("[server] starting listener on %s", c.String("listen-addr"))
		if err = server.ListenAndServe(); err != nil {
			glog.Fatalf("[server] %v", err)
		}
	},
}

type atomicHandler struct {
	Context context.Context
	Current http.Handler

	Mutex    *sync.RWMutex
	Watching bool
	Routes   []string
}

// FlushCache - Flushes routes cache. Reads them and constructs handler.
func (handler *atomicHandler) FlushCache() {
	// Construct handler from routes
	h, err := handler.construct()
	if err != nil {
		glog.Fatalf("[routes] %v", err)
	}

	// Lock mutex and exchange handler
	handler.Mutex.Lock()
	handler.Current = h
	handler.Mutex.Unlock()
}

func (handler *atomicHandler) construct() (_ http.Handler, err error) {
	h, err := constructHandler(handler.Routes...)
	if err != nil {
		return
	}
	return xhandler.New(handler.Context, h), nil
}

func (handler *atomicHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// If not watching don't use mutexe
	if !handler.Watching {
		handler.Current.ServeHTTP(w, r)
		return
	}

	// Lock for read and get handler
	handler.Mutex.RLock()
	h := handler.Current
	handler.Mutex.RUnlock()

	// Serve request
	h.ServeHTTP(w, r)
}

func constructHandler(filenames ...string) (_ xhandler.HandlerC, err error) {
	if len(filenames) == 0 {
		return renderweb.New(), nil
	}

	routes, err := constructRoutes(filenames...)
	if err != nil {
		return
	}

	// Turn routes into HTTP handler
	return routes.Construct()
}

// constructRoutes - Constructs routes map from multiple filenames.
func constructRoutes(filenames ...string) (res renderweb.Routes, err error) {
	res = make(renderweb.Routes)
	for _, filename := range filenames {
		var routes renderweb.Routes
		routes, err = renderweb.RoutesFromFile(filename)
		if err != nil {
			return
		}
		for route, handler := range routes {
			if _, exists := res[route]; exists {
				return nil, fmt.Errorf("route %q in %q is not unique", route, filename)
			}
			res[route] = handler
		}
	}
	return
}
<commit_msg>command: initials for renderweb options<commit_after>package command

import (
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/codegangsta/cli"
	"github.com/golang/glog"
	"github.com/rs/xhandler"
	"golang.org/x/net/context"

	"github.com/crackcomm/renderer/compiler"
	"github.com/crackcomm/renderer/renderweb"
	"github.com/crackcomm/renderer/storage"
	"github.com/crackcomm/renderer/watcher"

	// Profiler
	_ "net/http/pprof"
)

// Commands - List of renderer commands.
var Commands = []cli.Command{
	Web,
}

// Web - Web command.
var Web = cli.Command{
	Name:  "web",
	Usage: "starts renderer web API",
	Flags: []cli.Flag{
		// Compiler options
		cli.StringSliceFlag{
			Name:  "routes",
			Usage: "file containing routes in yaml format",
		},
		cli.StringFlag{
			Name:  "components",
			Usage: "directory containing components",
		},
		cli.BoolFlag{
			Name:  "watch",
			Usage: "watch for changes in components",
		},
		cli.BoolFlag{
			Name:  "compress",
			Usage: "removes repeated whitespaces",
		},
		cli.DurationFlag{
			Name:  "cache-expiration",
			Usage: "cache expiration time",
			Value: 15 * time.Minute,
		},
		cli.DurationFlag{
			Name:  "cache-cleanup",
			Usage: "cache cleanup interval",
			Value: 5 * time.Minute,
		},

		// Web server options
		cli.StringFlag{
			Name:  "listen-addr",
			Usage: "web interface listening address",
			Value: "127.0.0.1:6660",
		},
		cli.DurationFlag{
			Name:  "render-timeout",
			Usage: "component render timeout",
			Value: 5 * time.Second,
		},

		// HTTP server flags
		cli.DurationFlag{
			Name:   "http-read-timeout",
			EnvVar: "HTTP_READ_TIMEOUT",
			Usage:  "http server read timeout",
			Value:  time.Minute,
		},
		cli.DurationFlag{
			Name:   "http-write-timeout",
			EnvVar: "HTTP_WRITE_TIMEOUT",
			Usage:  "http server write timeout",
			Value:  time.Minute,
		},

		// Profiler
		cli.StringFlag{
			Name:  "pprof-addr",
			Usage: "pprof listening address",
		},
	},
	Action: func(c *cli.Context) {
		// Get components directory from --components flag
		// Print fatal error if not set
		if c.String("components") == "" {
			glog.Fatal("--components flag cannot be empty")
		}

		// Create a new storage in directory from --components flag
		storage, err := storage.New(
			storage.WithDir(c.String("components")),
			storage.WithCacheExpiration(c.Duration("cache-expiration")),
			storage.WithCacheCleanupInterval(c.Duration("cache-cleanup")),
			storage.WithWhitespaceRemoval(c.Bool("compress")),
		)
		if err != nil {
			glog.Fatalf("[storage] %v", err)
		}
		defer storage.Close()

		// Create a compiler from storage
		comp := compiler.New(storage)

		// Create a context with compiler
		ctx := compiler.NewContext(context.Background(), comp)

		// Render web options
		renderOpts := []renderweb.Option{
		// renderweb.WithDefaultTemplateContext(DefaultTemplateContext),
		}

		// Turn routes into HTTP handler
		api, err := constructHandler(c.StringSlice("routes"), renderOpts)
		if err != nil {
			glog.Fatalf("[routes] %v", err)
		}

		// Construct API handler
		handler := &atomicHandler{
			Context:  ctx,
			Current:  xhandler.New(ctx, api),
			Options:  renderOpts,
			Watching: c.Bool("watch"),
			Routes:   c.StringSlice("routes"),
			Mutex:    new(sync.RWMutex),
		}

		if c.Bool("watch") {
			// Start watching for changes in components directory
			var w *watcher.Watcher
			w, err = watcher.Start(c.String("components"), storage)
			if err != nil {
				glog.Fatal(err)
			}
			defer w.Stop()

			// Start watching for changes in routes
			for _, filename := range c.StringSlice("routes") {
				var watch *watcher.Watcher
				watch, err = watcher.Start(filename, handler)
				if err != nil {
					glog.Fatal(err)
				}
				defer watch.Stop()
			}
		}

		// Start profiler if enabled
		if pprofaddr := c.String("pprof-addr"); pprofaddr != "" {
			go func() {
				glog.Infof("[pprof] starting listener on %s", pprofaddr)
				if err := http.ListenAndServe(pprofaddr, nil); err != nil {
					glog.Fatal(err)
				}
			}()
		}

		// Construct http server
		server := &http.Server{
			Addr:           c.String("listen-addr"),
			Handler:        handler,
			ReadTimeout:    c.Duration("http-read-timeout"),
			WriteTimeout:   c.Duration("http-write-timeout"),
			MaxHeaderBytes: 64 * 1024,
		}

		glog.Infof("[server] starting listener on %s", c.String("listen-addr"))
		if err = server.ListenAndServe(); err != nil {
			glog.Fatalf("[server] %v", err)
		}
	},
}

type atomicHandler struct {
	Context context.Context
	Current http.Handler
	Options []renderweb.Option

	Mutex    *sync.RWMutex
	Watching bool
	Routes   []string
}

// FlushCache - Flushes routes cache. Reads them and constructs handler.
func (handler *atomicHandler) FlushCache() {
	// Construct handler from routes
	h, err := handler.construct()
	if err != nil {
		glog.Fatalf("[routes] %v", err)
	}

	// Lock mutex and exchange handler
	handler.Mutex.Lock()
	handler.Current = h
	handler.Mutex.Unlock()
}

func (handler *atomicHandler) construct() (_ http.Handler, err error) {
	h, err := constructHandler(handler.Routes, handler.Options)
	if err != nil {
		return
	}
	return xhandler.New(handler.Context, h), nil
}

func (handler *atomicHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// If not watching don't use mutexe
	if !handler.Watching {
		handler.Current.ServeHTTP(w, r)
		return
	}

	// Lock for read and get handler
	handler.Mutex.RLock()
	h := handler.Current
	handler.Mutex.RUnlock()

	// Serve request
	h.ServeHTTP(w, r)
}

func constructHandler(filenames []string, options []renderweb.Option) (_ xhandler.HandlerC, err error) {
	if len(filenames) == 0 {
		return renderweb.New(), nil
	}

	routes, err := constructRoutes(filenames, options)
	if err != nil {
		return
	}

	// Turn routes into HTTP handler
	return routes.Construct(options...)
}

// constructRoutes - Constructs routes map from multiple filenames.
func constructRoutes(filenames []string, options []renderweb.Option) (res renderweb.Routes, err error) {
	res = make(renderweb.Routes)
	for _, filename := range filenames {
		var routes renderweb.Routes
		routes, err = renderweb.RoutesFromFile(filename)
		if err != nil {
			return
		}
		for route, handler := range routes {
			if _, exists := res[route]; exists {
				return nil, fmt.Errorf("route %q in %q is not unique", route, filename)
			}
			res[route] = handler
		}
	}
	return
}
<|endoftext|>


<commit_before>package command

type Command []byte

type StateMachine interface {
	Execute(c []Command) ([]interface{}, error)
	HaveConflicts(c1 []Command, c2 []Command) bool
}
<commit_msg>add two comments<commit_after>package command

type Command []byte

type StateMachine interface {
	// Execute a group of commands, return the result
	// in an array, return any error that occurs
	Execute(c []Command) ([]interface{}, error)
	// Test if there exists any conflicts in two group of commands
	HaveConflicts(c1 []Command, c2 []Command) bool
}
<|endoftext|>


<commit_before>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/openfaas/faas-cli/proxy"
	"github.com/openfaas/faas-cli/stack"
	"github.com/spf13/cobra"
)

// DeployFlags holds flags that are to be added to commands.
type DeployFlags struct {
	envvarOpts  []string
	replace     bool
	update      bool
	constraints []string
	secrets     []string
	labelOpts   []string
}

var deployFlags DeployFlags

func init() {
	// Setup flags that are used by multiple commands (variables defined in faas.go)
	deployCmd.Flags().StringVar(&fprocess, "fprocess", "", "fprocess value to be run as a serverless function by the watchdog")
	deployCmd.Flags().StringVarP(&gateway, "gateway", "g", defaultGateway, "Gateway URL starting with http(s)://")
	deployCmd.Flags().StringVar(&handler, "handler", "", "Directory with handler for function, e.g. handler.js")
	deployCmd.Flags().StringVar(&image, "image", "", "Docker image name to build")
	deployCmd.Flags().StringVar(&language, "lang", "", "Programming language template")
	deployCmd.Flags().StringVar(&functionName, "name", "", "Name of the deployed function")
	deployCmd.Flags().StringVar(&network, "network", defaultNetwork, "Name of the network")

	// Setup flags that are used only by this command (variables defined above)
	deployCmd.Flags().StringArrayVarP(&deployFlags.envvarOpts, "env", "e", []string{}, "Set one or more environment variables (ENVVAR=VALUE)")

	deployCmd.Flags().StringArrayVarP(&deployFlags.labelOpts, "label", "l", []string{}, "Set one or more label (LABEL=VALUE)")

	deployCmd.Flags().BoolVar(&deployFlags.replace, "replace", false, "Remove and re-create existing function(s)")
	deployCmd.Flags().BoolVar(&deployFlags.update, "update", true, "Perform rolling update on existing function(s)")

	deployCmd.Flags().StringArrayVar(&deployFlags.constraints, "constraint", []string{}, "Apply a constraint to the function")
	deployCmd.Flags().StringArrayVar(&deployFlags.secrets, "secret", []string{}, "Give the function access to a secure secret")

	// Set bash-completion.
	_ = deployCmd.Flags().SetAnnotation("handler", cobra.BashCompSubdirsInDir, []string{})

	faasCmd.AddCommand(deployCmd)
}

// deployCmd handles deploying OpenFaaS function containers
var deployCmd = &cobra.Command{
	Use: `deploy -f YAML_FILE [--replace=false]
  faas-cli deploy --image IMAGE_NAME
                  --name FUNCTION_NAME
                  [--lang <ruby|python|node|csharp>]
                  [--gateway GATEWAY_URL]
                  [--network NETWORK_NAME]
                  [--handler HANDLER_DIR]
                  [--fprocess PROCESS]
                  [--env ENVVAR=VALUE ...]
                  [--label LABEL=VALUE ...]
				  [--replace=false]
				  [--update=false]
                  [--constraint PLACEMENT_CONSTRAINT ...]
                  [--regex "REGEX"]
                  [--filter "WILDCARD"]
				  [--secret "SECRET_NAME"]`,

	Short: "Deploy OpenFaaS functions",
	Long: `Deploys OpenFaaS function containers either via the supplied YAML config using
the "--yaml" flag (which may contain multiple function definitions), or directly
via flags. Note: --replace and --update are mutually exclusive.`,
	Example: `  faas-cli deploy -f https://domain/path/myfunctions.yml
  faas-cli deploy -f ./stack.yml
  faas-cli deploy -f ./stack.yml --label canary=true
  faas-cli deploy -f ./stack.yml --filter "*gif*" --secret dockerhuborg
  faas-cli deploy -f ./stack.yml --regex "fn[0-9]_.*"
  faas-cli deploy -f ./stack.yml --replace=false --update=true
  faas-cli deploy -f ./stack.yml --replace=true --update=false
  faas-cli deploy --image=alexellis/faas-url-ping --name=url-ping
  faas-cli deploy --image=my_image --name=my_fn --handler=/path/to/fn/
                  --gateway=http://remote-site.com:8080 --lang=python
                  --env=MYVAR=myval`,
	PreRunE: preRunDeploy,
	RunE:    runDeploy,
}

// preRunDeploy validates args & flags
func preRunDeploy(cmd *cobra.Command, args []string) error {
	language, _ = validateLanguageFlag(language)

	return nil
}

func runDeploy(cmd *cobra.Command, args []string) error {
	return runDeployCommand(args, image, fprocess, functionName, deployFlags)
}

func runDeployCommand(args []string, image string, fprocess string, functionName string, deployFlags DeployFlags) error {
	if deployFlags.update && deployFlags.replace {
		fmt.Println(`Cannot specify --update and --replace at the same time. One of --update or --replace must be false.
  --replace    removes an existing deployment before re-creating it
  --update     performs a rolling update to a new function image or configuration (default true)`)
		return fmt.Errorf("cannot specify --update and --replace at the same time")
	}

	var services stack.Services
	if len(yamlFile) > 0 {
		parsedServices, err := stack.ParseYAMLFile(yamlFile, regex, filter)
		if err != nil {
			return err
		}

		parsedServices.Provider.GatewayURL = getGatewayURL(gateway, defaultGateway, parsedServices.Provider.GatewayURL, os.Getenv(openFaaSURLEnvironment))

		// Override network if passed
		if len(network) > 0 && network != defaultNetwork {
			parsedServices.Provider.Network = network
		}

		if parsedServices != nil {
			services = *parsedServices
		}
	}

	if len(services.Functions) > 0 {
		if len(services.Provider.Network) == 0 {
			services.Provider.Network = defaultNetwork
		}

		for k, function := range services.Functions {

			function.Name = k
			fmt.Printf("Deploying: %s.\n", function.Name)

			var functionConstraints []string
			if function.Constraints != nil {
				functionConstraints = *function.Constraints
			} else if len(deployFlags.constraints) > 0 {
				functionConstraints = deployFlags.constraints
			}

			if len(function.Secrets) > 0 {
				deployFlags.secrets = mergeSlice(function.Secrets, deployFlags.secrets)
			}

			fileEnvironment, err := readFiles(function.EnvironmentFile)
			if err != nil {
				return err
			}

			labelMap := map[string]string{}
			if function.Labels != nil {
				labelMap = *function.Labels
			}

			labelArgumentMap, labelErr := parseMap(deployFlags.labelOpts, "label")
			if labelErr != nil {
				return fmt.Errorf("error parsing labels: %v", labelErr)
			}

			allLabels := mergeMap(labelMap, labelArgumentMap)

			allEnvironment, envErr := compileEnvironment(deployFlags.envvarOpts, function.Environment, fileEnvironment)
			if envErr != nil {
				return envErr
			}

			// Get FProcess to use from the ./template/template.yml, if a template is being used
			if languageExistsNotDockerfile(function.Language) {
				var fprocessErr error

				function.FProcess, fprocessErr = deriveFprocess(function)
				if fprocessErr != nil {
					return fmt.Errorf(`template directory may be missing or invalid, please run "faas template pull"
Error: %s`, fprocessErr.Error())
				}
			}

			functionResourceRequest1 := proxy.FunctionResourceRequest{
				Limits:   function.Limits,
				Requests: function.Requests,
			}

			proxy.DeployFunction(function.FProcess, services.Provider.GatewayURL, function.Name, function.Image, function.Language, deployFlags.replace, allEnvironment, services.Provider.Network, functionConstraints, deployFlags.update, deployFlags.secrets, allLabels, functionResourceRequest1)
		}
	} else {
		if len(image) == 0 {
			return fmt.Errorf("please provide a --image to be deployed")
		}
		if len(functionName) == 0 {
			return fmt.Errorf("please provide a --name for your function as it will be deployed on FaaS")
		}

		if err := deployImage(image, fprocess, functionName, deployFlags); err != nil {
			return err
		}
	}

	return nil
}

// deployImage deploys a function with the given image
func deployImage(
	image string,
	fprocess string,
	functionName string,
	deployFlags DeployFlags,
) error {
	envvars, err := parseMap(deployFlags.envvarOpts, "env")
	if err != nil {
		return fmt.Errorf("error parsing envvars: %v", err)
	}

	labelMap, labelErr := parseMap(deployFlags.labelOpts, "label")
	if labelErr != nil {
		return fmt.Errorf("error parsing labels: %v", labelErr)
	}

	functionResourceRequest1 := proxy.FunctionResourceRequest{}
	proxy.DeployFunction(fprocess, gateway, functionName, image, language, deployFlags.replace, envvars, network, deployFlags.constraints, deployFlags.update, deployFlags.secrets, labelMap, functionResourceRequest1)

	return nil
}

func mergeSlice(values []string, overlay []string) []string {
	results := []string{}
	added := make(map[string]bool)
	for _, value := range overlay {
		results = append(results, value)
		added[value] = true
	}

	for _, value := range values {
		if exists := added[value]; exists == false {
			results = append(results, value)
		}
	}

	return results
}

func readFiles(files []string) (map[string]string, error) {
	envs := make(map[string]string)

	for _, file := range files {
		bytesOut, readErr := ioutil.ReadFile(file)
		if readErr != nil {
			return nil, readErr
		}

		envFile := stack.EnvironmentFile{}
		unmarshalErr := yaml.Unmarshal(bytesOut, &envFile)
		if unmarshalErr != nil {
			return nil, unmarshalErr
		}
		for k, v := range envFile.Environment {
			envs[k] = v
		}
	}
	return envs, nil
}

func parseMap(envvars []string, keyName string) (map[string]string, error) {
	result := make(map[string]string)
	for _, envvar := range envvars {
		s := strings.SplitN(strings.TrimSpace(envvar), "=", 2)
		if len(s) != 2 {
			return nil, fmt.Errorf("label format is not correct, needs key=value")
		}
		envvarName := s[0]
		envvarValue := s[1]

		if !(len(envvarName) > 0) {
			return nil, fmt.Errorf("empty %s name: [%s]", keyName, envvar)
		}
		if !(len(envvarValue) > 0) {
			return nil, fmt.Errorf("empty %s value: [%s]", keyName, envvar)
		}

		result[envvarName] = envvarValue
	}
	return result, nil
}

func mergeMap(i map[string]string, j map[string]string) map[string]string {
	merged := make(map[string]string)

	for k, v := range i {
		merged[k] = v
	}
	for k, v := range j {
		merged[k] = v
	}
	return merged
}

func compileEnvironment(envvarOpts []string, yamlEnvironment map[string]string, fileEnvironment map[string]string) (map[string]string, error) {
	envvarArguments, err := parseMap(envvarOpts, "env")
	if err != nil {
		return nil, fmt.Errorf("error parsing envvars: %v", err)
	}

	functionAndStack := mergeMap(yamlEnvironment, fileEnvironment)
	return mergeMap(functionAndStack, envvarArguments), nil
}

func deriveFprocess(function stack.Function) (string, error) {
	var fprocess string

	pathToTemplateYAML := "./template/" + function.Language + "/template.yml"
	if _, err := os.Stat(pathToTemplateYAML); os.IsNotExist(err) {
		return "", err
	}

	var langTemplate stack.LanguageTemplate
	parsedLangTemplate, err := stack.ParseYAMLForLanguageTemplate(pathToTemplateYAML)

	if err != nil {
		return "", err

	}

	if parsedLangTemplate != nil {
		langTemplate = *parsedLangTemplate
		fprocess = langTemplate.FProcess
	}

	return fprocess, nil
}

func languageExistsNotDockerfile(language string) bool {
	return len(language) > 0 && strings.ToLower(language) != "dockerfile"
}
<commit_msg>Prepend URL scheme with manual deployments<commit_after>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/openfaas/faas-cli/proxy"
	"github.com/openfaas/faas-cli/stack"
	"github.com/spf13/cobra"
)

// DeployFlags holds flags that are to be added to commands.
type DeployFlags struct {
	envvarOpts  []string
	replace     bool
	update      bool
	constraints []string
	secrets     []string
	labelOpts   []string
}

var deployFlags DeployFlags

func init() {
	// Setup flags that are used by multiple commands (variables defined in faas.go)
	deployCmd.Flags().StringVar(&fprocess, "fprocess", "", "fprocess value to be run as a serverless function by the watchdog")
	deployCmd.Flags().StringVarP(&gateway, "gateway", "g", defaultGateway, "Gateway URL starting with http(s)://")
	deployCmd.Flags().StringVar(&handler, "handler", "", "Directory with handler for function, e.g. handler.js")
	deployCmd.Flags().StringVar(&image, "image", "", "Docker image name to build")
	deployCmd.Flags().StringVar(&language, "lang", "", "Programming language template")
	deployCmd.Flags().StringVar(&functionName, "name", "", "Name of the deployed function")
	deployCmd.Flags().StringVar(&network, "network", defaultNetwork, "Name of the network")

	// Setup flags that are used only by this command (variables defined above)
	deployCmd.Flags().StringArrayVarP(&deployFlags.envvarOpts, "env", "e", []string{}, "Set one or more environment variables (ENVVAR=VALUE)")

	deployCmd.Flags().StringArrayVarP(&deployFlags.labelOpts, "label", "l", []string{}, "Set one or more label (LABEL=VALUE)")

	deployCmd.Flags().BoolVar(&deployFlags.replace, "replace", false, "Remove and re-create existing function(s)")
	deployCmd.Flags().BoolVar(&deployFlags.update, "update", true, "Perform rolling update on existing function(s)")

	deployCmd.Flags().StringArrayVar(&deployFlags.constraints, "constraint", []string{}, "Apply a constraint to the function")
	deployCmd.Flags().StringArrayVar(&deployFlags.secrets, "secret", []string{}, "Give the function access to a secure secret")

	// Set bash-completion.
	_ = deployCmd.Flags().SetAnnotation("handler", cobra.BashCompSubdirsInDir, []string{})

	faasCmd.AddCommand(deployCmd)
}

// deployCmd handles deploying OpenFaaS function containers
var deployCmd = &cobra.Command{
	Use: `deploy -f YAML_FILE [--replace=false]
  faas-cli deploy --image IMAGE_NAME
                  --name FUNCTION_NAME
                  [--lang <ruby|python|node|csharp>]
                  [--gateway GATEWAY_URL]
                  [--network NETWORK_NAME]
                  [--handler HANDLER_DIR]
                  [--fprocess PROCESS]
                  [--env ENVVAR=VALUE ...]
                  [--label LABEL=VALUE ...]
				  [--replace=false]
				  [--update=false]
                  [--constraint PLACEMENT_CONSTRAINT ...]
                  [--regex "REGEX"]
                  [--filter "WILDCARD"]
				  [--secret "SECRET_NAME"]`,

	Short: "Deploy OpenFaaS functions",
	Long: `Deploys OpenFaaS function containers either via the supplied YAML config using
the "--yaml" flag (which may contain multiple function definitions), or directly
via flags. Note: --replace and --update are mutually exclusive.`,
	Example: `  faas-cli deploy -f https://domain/path/myfunctions.yml
  faas-cli deploy -f ./stack.yml
  faas-cli deploy -f ./stack.yml --label canary=true
  faas-cli deploy -f ./stack.yml --filter "*gif*" --secret dockerhuborg
  faas-cli deploy -f ./stack.yml --regex "fn[0-9]_.*"
  faas-cli deploy -f ./stack.yml --replace=false --update=true
  faas-cli deploy -f ./stack.yml --replace=true --update=false
  faas-cli deploy --image=alexellis/faas-url-ping --name=url-ping
  faas-cli deploy --image=my_image --name=my_fn --handler=/path/to/fn/
                  --gateway=http://remote-site.com:8080 --lang=python
                  --env=MYVAR=myval`,
	PreRunE: preRunDeploy,
	RunE:    runDeploy,
}

// preRunDeploy validates args & flags
func preRunDeploy(cmd *cobra.Command, args []string) error {
	language, _ = validateLanguageFlag(language)

	return nil
}

func runDeploy(cmd *cobra.Command, args []string) error {
	return runDeployCommand(args, image, fprocess, functionName, deployFlags)
}

func runDeployCommand(args []string, image string, fprocess string, functionName string, deployFlags DeployFlags) error {
	if deployFlags.update && deployFlags.replace {
		fmt.Println(`Cannot specify --update and --replace at the same time. One of --update or --replace must be false.
  --replace    removes an existing deployment before re-creating it
  --update     performs a rolling update to a new function image or configuration (default true)`)
		return fmt.Errorf("cannot specify --update and --replace at the same time")
	}

	var services stack.Services
	if len(yamlFile) > 0 {
		parsedServices, err := stack.ParseYAMLFile(yamlFile, regex, filter)
		if err != nil {
			return err
		}

		parsedServices.Provider.GatewayURL = getGatewayURL(gateway, defaultGateway, parsedServices.Provider.GatewayURL, os.Getenv(openFaaSURLEnvironment))

		// Override network if passed
		if len(network) > 0 && network != defaultNetwork {
			parsedServices.Provider.Network = network
		}

		if parsedServices != nil {
			services = *parsedServices
		}
	}

	if len(services.Functions) > 0 {
		if len(services.Provider.Network) == 0 {
			services.Provider.Network = defaultNetwork
		}

		for k, function := range services.Functions {

			function.Name = k
			fmt.Printf("Deploying: %s.\n", function.Name)

			var functionConstraints []string
			if function.Constraints != nil {
				functionConstraints = *function.Constraints
			} else if len(deployFlags.constraints) > 0 {
				functionConstraints = deployFlags.constraints
			}

			if len(function.Secrets) > 0 {
				deployFlags.secrets = mergeSlice(function.Secrets, deployFlags.secrets)
			}

			fileEnvironment, err := readFiles(function.EnvironmentFile)
			if err != nil {
				return err
			}

			labelMap := map[string]string{}
			if function.Labels != nil {
				labelMap = *function.Labels
			}

			labelArgumentMap, labelErr := parseMap(deployFlags.labelOpts, "label")
			if labelErr != nil {
				return fmt.Errorf("error parsing labels: %v", labelErr)
			}

			allLabels := mergeMap(labelMap, labelArgumentMap)

			allEnvironment, envErr := compileEnvironment(deployFlags.envvarOpts, function.Environment, fileEnvironment)
			if envErr != nil {
				return envErr
			}

			// Get FProcess to use from the ./template/template.yml, if a template is being used
			if languageExistsNotDockerfile(function.Language) {
				var fprocessErr error

				function.FProcess, fprocessErr = deriveFprocess(function)
				if fprocessErr != nil {
					return fmt.Errorf(`template directory may be missing or invalid, please run "faas template pull"
Error: %s`, fprocessErr.Error())
				}
			}

			functionResourceRequest1 := proxy.FunctionResourceRequest{
				Limits:   function.Limits,
				Requests: function.Requests,
			}

			proxy.DeployFunction(function.FProcess, services.Provider.GatewayURL, function.Name, function.Image, function.Language, deployFlags.replace, allEnvironment, services.Provider.Network, functionConstraints, deployFlags.update, deployFlags.secrets, allLabels, functionResourceRequest1)
		}
	} else {
		if len(image) == 0 {
			return fmt.Errorf("please provide a --image to be deployed")
		}
		if len(functionName) == 0 {
			return fmt.Errorf("please provide a --name for your function as it will be deployed on FaaS")
		}

		gateway = getGatewayURL(gateway, defaultGateway, gateway, os.Getenv(openFaaSURLEnvironment))

		if err := deployImage(image, fprocess, functionName, deployFlags); err != nil {
			return err
		}
	}

	return nil
}

// deployImage deploys a function with the given image
func deployImage(
	image string,
	fprocess string,
	functionName string,
	deployFlags DeployFlags,
) error {
	envvars, err := parseMap(deployFlags.envvarOpts, "env")
	if err != nil {
		return fmt.Errorf("error parsing envvars: %v", err)
	}

	labelMap, labelErr := parseMap(deployFlags.labelOpts, "label")
	if labelErr != nil {
		return fmt.Errorf("error parsing labels: %v", labelErr)
	}

	functionResourceRequest1 := proxy.FunctionResourceRequest{}
	proxy.DeployFunction(fprocess, gateway, functionName, image, language, deployFlags.replace, envvars, network, deployFlags.constraints, deployFlags.update, deployFlags.secrets, labelMap, functionResourceRequest1)

	return nil
}

func mergeSlice(values []string, overlay []string) []string {
	results := []string{}
	added := make(map[string]bool)
	for _, value := range overlay {
		results = append(results, value)
		added[value] = true
	}

	for _, value := range values {
		if exists := added[value]; exists == false {
			results = append(results, value)
		}
	}

	return results
}

func readFiles(files []string) (map[string]string, error) {
	envs := make(map[string]string)

	for _, file := range files {
		bytesOut, readErr := ioutil.ReadFile(file)
		if readErr != nil {
			return nil, readErr
		}

		envFile := stack.EnvironmentFile{}
		unmarshalErr := yaml.Unmarshal(bytesOut, &envFile)
		if unmarshalErr != nil {
			return nil, unmarshalErr
		}
		for k, v := range envFile.Environment {
			envs[k] = v
		}
	}
	return envs, nil
}

func parseMap(envvars []string, keyName string) (map[string]string, error) {
	result := make(map[string]string)
	for _, envvar := range envvars {
		s := strings.SplitN(strings.TrimSpace(envvar), "=", 2)
		if len(s) != 2 {
			return nil, fmt.Errorf("label format is not correct, needs key=value")
		}
		envvarName := s[0]
		envvarValue := s[1]

		if !(len(envvarName) > 0) {
			return nil, fmt.Errorf("empty %s name: [%s]", keyName, envvar)
		}
		if !(len(envvarValue) > 0) {
			return nil, fmt.Errorf("empty %s value: [%s]", keyName, envvar)
		}

		result[envvarName] = envvarValue
	}
	return result, nil
}

func mergeMap(i map[string]string, j map[string]string) map[string]string {
	merged := make(map[string]string)

	for k, v := range i {
		merged[k] = v
	}
	for k, v := range j {
		merged[k] = v
	}
	return merged
}

func compileEnvironment(envvarOpts []string, yamlEnvironment map[string]string, fileEnvironment map[string]string) (map[string]string, error) {
	envvarArguments, err := parseMap(envvarOpts, "env")
	if err != nil {
		return nil, fmt.Errorf("error parsing envvars: %v", err)
	}

	functionAndStack := mergeMap(yamlEnvironment, fileEnvironment)
	return mergeMap(functionAndStack, envvarArguments), nil
}

func deriveFprocess(function stack.Function) (string, error) {
	var fprocess string

	pathToTemplateYAML := "./template/" + function.Language + "/template.yml"
	if _, err := os.Stat(pathToTemplateYAML); os.IsNotExist(err) {
		return "", err
	}

	var langTemplate stack.LanguageTemplate
	parsedLangTemplate, err := stack.ParseYAMLForLanguageTemplate(pathToTemplateYAML)

	if err != nil {
		return "", err

	}

	if parsedLangTemplate != nil {
		langTemplate = *parsedLangTemplate
		fprocess = langTemplate.FProcess
	}

	return fprocess, nil
}

func languageExistsNotDockerfile(language string) bool {
	return len(language) > 0 && strings.ToLower(language) != "dockerfile"
}
<|endoftext|>


<commit_before>// Package chartjs simplifies making chartjs.org plots in go.
package chartjs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"math"

	"github.com/brentp/go-chartjs/types"
)

var True = types.True
var False = types.False

var chartTypes = [...]string{
	"line",
	"bar",
	"bubble",
}

type chartType int

func (c chartType) MarshalJSON() ([]byte, error) {
	return []byte(`"` + chartTypes[c] + `"`), nil
}

const (
	// Line is a "line" plot
	Line chartType = iota
	// Bar is a "bar" plot
	Bar
	// Bubble is a "bubble" plot
	Bubble
)

type interpMode int

const (
	_ interpMode = iota
	InterpMonotone
	InterpDefault
)

var interpModes = [...]string{
	"",
	"monotone",
	"default",
}

func (m interpMode) MarshalJSON() ([]byte, error) {
	return []byte(`"` + interpModes[m] + `"`), nil
}

// XFloatFormat determines how many decimal places are sent in the JSON for X values.
var XFloatFormat = "%.2f"

// YFloatFormat determines how many decimal places are sent in the JSON for Y values.
var YFloatFormat = "%.2f"

// Values dictates the interface of data to be plotted.
type Values interface {
	// X-axis values. If only these are specified then it must be a Bar plot.
	Xs() []float64
	// Optional Y values.
	Ys() []float64
	// Rs are used to size points for chartType `Bubble`
	Rs() []float64
}

func marshalValuesJSON(v Values) ([]byte, error) {
	xs, ys, rs := v.Xs(), v.Ys(), v.Rs()
	if len(xs) == 0 {
		if len(rs) != 0 {
			return nil, fmt.Errorf("chart: bad format of Values data")
		}
		xs = ys[:len(ys)]
		ys = nil
	}
	buf := bytes.NewBuffer(make([]byte, 0, 8*len(xs)))
	buf.WriteRune('[')
	if len(rs) > 0 {
		if len(xs) != len(ys) || len(xs) != len(rs) {
			return nil, fmt.Errorf("chart: bad format of Values. All axes must be of the same length")
		}
		var err error
		for i, x := range xs {
			if i > 0 {
				buf.WriteRune(',')
			}
			y, r := ys[i], rs[i]
			if math.IsNaN(y) {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + XFloatFormat + ",\"y\": null,\"r\":" + YFloatFormat + "}"), x, r))
			} else {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + XFloatFormat + ",\"y\":" + YFloatFormat + ",\"r\":" + YFloatFormat + "}"), x, y, r))
			}
			if err != nil {
				return nil, err
			}
		}
	} else if len(ys) > 0 {
		if len(xs) != len(ys) {
			return nil, fmt.Errorf("chart: bad format of Values. X and Y must be of the same length")
		}
		var err error
		for i, x := range xs {
			if i > 0 {
				buf.WriteRune(',')
			}
			y := ys[i]
			if math.IsNaN(y) {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + XFloatFormat + ",\"y\": null }"), x))
			} else {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + XFloatFormat + ",\"y\":" + YFloatFormat + "}"), x, y))
			}
			if err != nil {
				return nil, err
			}
		}

	} else {
		for i, x := range xs {
			if i > 0 {
				buf.WriteRune(',')
			}
			_, err := buf.WriteString(fmt.Sprintf(XFloatFormat, x))
			if err != nil {
				return nil, err
			}
		}
	}

	buf.WriteRune(']')
	return buf.Bytes(), nil
}

// shape indicates the type of marker used for plotting.
type shape int

var shapes = []string{
	"",
	"circle",
	"triangle",
	"rect",
	"rectRot",
	"cross",
	"crossRot",
	"star",
	"line",
	"dash",
}

const (
	empty = iota
	Circle
	Triangle
	Rect
	RectRot
	Cross
	CrossRot
	Star
	LinePoint
	Dash
)

func (s shape) MarshalJSON() ([]byte, error) {
	return []byte(`"` + shapes[s] + `"`), nil
}

// Dataset wraps the "dataset" JSON
type Dataset struct {
	Data            Values      `json:"-"`
	Type            chartType   `json:"type,omitempty"`
	BackgroundColor *types.RGBA `json:"backgroundColor,omitempty"`
	// BorderColor is the color of the line.
	BorderColor *types.RGBA `json:"borderColor,omitempty"`
	// BorderWidth is the width of the line.
	BorderWidth float64 `json:"borderWidth"`

	// Label indicates the name of the dataset to be shown in the legend.
	Label string     `json:"label,omitempty"`
	Fill  types.Bool `json:"fill,omitempty"`

	// SteppedLine of true means dont interpolate and ignore line tension.
	SteppedLine            types.Bool  `json:"steppedLine,omitempty"`
	LineTension            float64     `json:"lineTension"`
	CubicInterpolationMode interpMode  `json:"cubicInterpolationMode,omitempty"`
	PointBackgroundColor   *types.RGBA `json:"pointBackgroundColor,omitempty"`
	PointBorderColor       *types.RGBA `json:"pointBorderColor,omitempty"`
	PointBorderWidth       float64     `json:"pointBorderWidth"`
	PointRadius            float64     `json:"pointRadius"`
	PointHitRadius         float64     `json:"pointHitRadius"`
	PointHoverRadius       float64     `json:"pointHoverRadius"`
	PointHoverBorderColor  *types.RGBA `json:"pointHoverBorderColor,omitempty"`
	PointHoverBorderWidth  float64     `json:"pointHoverBorderWidth"`
	PointStyle             shape       `json:"pointStyle,omitempty"`

	ShowLine types.Bool `json:"showLine,omitempty"`
	SpanGaps types.Bool `json:"spanGaps,omitempty"`

	// Axis ID that matches the ID on the Axis where this dataset is to be drawn.
	XAxisID string `json:"xAxisID,omitempty"`
	YAxisID string `json:"yAxisID,omitempty"`
}

// MarshalJSON implements json.Marshaler interface.
func (d Dataset) MarshalJSON() ([]byte, error) {
	o, err := marshalValuesJSON(d.Data)
	// avoid recursion by creating an alias.
	type alias Dataset
	buf, err := json.Marshal(alias(d))
	if err != nil {
		return nil, err
	}
	// replace '}' with ',' to continue struct
	if len(buf) > 0 {
		buf[len(buf)-1] = ','
	}
	buf = append(buf, []byte(`"data":`)...)
	buf = append(buf, o...)
	buf = append(buf, '}')
	return buf, nil
}

// Data wraps the "data" JSON
type Data struct {
	Datasets []Dataset `json:"datasets"`
	Labels   []string  `json:"labels"`
}

type axisType int

var axisTypes = []string{
	"category",
	"linear",
	"logarithmic",
	"time",
	"radialLinear",
}

const (
	// Category is a categorical axis (this is the default),
	// used for bar plots.
	Category axisType = iota
	// Linear axis should be use for scatter plots.
	Linear
	// Log axis
	Log
	// Time axis
	Time
	// Radial axis
	Radial
)

func (t axisType) MarshalJSON() ([]byte, error) {
	return []byte("\"" + axisTypes[t] + "\""), nil
}

type axisPosition int

const (
	// Bottom puts the axis on the bottom (used for Y-axis)
	Bottom axisPosition = iota + 1
	// Top puts the axis on the bottom (used for Y-axis)
	Top
	// Left puts the axis on the bottom (used for X-axis)
	Left
	// Right puts the axis on the bottom (used for X-axis)
	Right
)

var axisPositions = []string{
	"",
	"bottom",
	"top",
	"left",
	"right",
}

func (p axisPosition) MarshalJSON() ([]byte, error) {
	return []byte(`"` + axisPositions[p] + `"`), nil
}

// Axis corresponds to 'scale' in chart.js lingo.
type Axis struct {
	Type      axisType     `json:"type"`
	Position  axisPosition `json:"position,omitempty"`
	Label     string       `json:"label,omitempty"`
	ID        string       `json:"id,omitempty"`
	GridLines types.Bool   `json:"gridLine,omitempty"`
	Stacked   types.Bool   `json:"stacked,omitempty"`

	// Bool differentiates between false and empty by use of pointer.
	Display    types.Bool  `json:"display,omitempty"`
	ScaleLabel *ScaleLabel `json:"scaleLabel,omitempty"`
	Tick       *Tick       `json:"ticks,omitempty"`
}

// Tick lets us set the range of the data.
type Tick struct {
	Min         float64    `json:"min,omitempty"`
	Max         float64    `json:"max,omitempty"`
	BeginAtZero types.Bool `json:"beginAtZero,omitempty"`
	// TODO: add additional options from: tick options.
}

// ScaleLabel corresponds to scale title.
// Display: True must be specified for this to be shown.
type ScaleLabel struct {
	Display     types.Bool  `json:"display,omitempty"`
	LabelString string      `json:"labelString,omitempty"`
	FontColor   *types.RGBA `json:"fontColor,omitempty"`
	FontFamily  string      `json:"fontFamily,omitempty"`
	FontSize    int         `json:"fontSize,omitempty"`
	FontStyle   string      `json:"fontStyle,omitempty"`
}

// Axes holds the X and Y axies. Its simpler to use Chart.AddXAxis, Chart.AddYAxis.
type Axes struct {
	XAxes []Axis `json:"xAxes,omitempty"`
	YAxes []Axis `json:"yAxes,omitempty"`
}

// AddX adds a X-Axis.
func (a *Axes) AddX(x Axis) {
	a.XAxes = append(a.XAxes, x)
}

// AddY adds a Y-Axis.
func (a *Axes) AddY(y Axis) {
	a.YAxes = append(a.YAxes, y)
}

// Option wraps the chartjs "option"
type Option struct {
	Responsive          types.Bool `json:"responsive,omitempty"`
	MaintainAspectRatio types.Bool `json:"maintainAspectRatio,omitempty"`
	Title               *Title     `json:"title,omitempty"`
}

// Title is the Options title
type Title struct {
	Display types.Bool `json:"display,omitempty"`
	Text    string     `json:"text,omitempty"`
}

// Options wraps the chartjs "options"
type Options struct {
	Option
	Scales  Axes     `json:"scales,omitempty"`
	Legend  *Legend  `json:"legend,omitempty"`
	Tooltip *Tooltip `json:"tooltips,omitempty"`
}

// Tooltip wraps chartjs "tooltips".
// TODO: figure out how to make this work.
type Tooltip struct {
	Enabled   types.Bool `json:"enabled,omitempty"`
	Intersect types.Bool `json:"intersect,omitempty"`
	// TODO: make mode typed by Interaction modes.
	Mode   string         `json:"mode,omitempty"`
	Custom template.JSStr `json:"custom,omitempty"`
}

type Legend struct {
	Display types.Bool `json:"display,omitempty"`
}

// Chart is the top-level type from chartjs.
type Chart struct {
	Type    chartType `json:"type"`
	Label   string    `json:"label,omitempty"`
	Data    Data      `json:"data,omitempty"`
	Options Options   `json:"options,omitempty"`
}

// AddDataset adds a dataset to the chart.
func (c *Chart) AddDataset(d Dataset) {
	c.Data.Datasets = append(c.Data.Datasets, d)
}

// AddXAxis adds an x-axis to the chart and returns the ID of the added axis.
func (c *Chart) AddXAxis(x Axis) (string, error) {
	if x.ID == "" {
		x.ID = fmt.Sprintf("xaxis%d", len(c.Options.Scales.XAxes))
	}
	if x.Position == Left || x.Position == Right {
		return "", fmt.Errorf("chart: added x-axis to left or right")
	}
	c.Options.Scales.XAxes = append(c.Options.Scales.XAxes, x)
	return x.ID, nil
}

// AddYAxis adds an y-axis to the chart and return the ID of the added axis.
func (c *Chart) AddYAxis(y Axis) (string, error) {
	if y.ID == "" {
		y.ID = fmt.Sprintf("yaxis%d", len(c.Options.Scales.YAxes))
	}
	if y.Position == Top || y.Position == Bottom {
		return "", fmt.Errorf("chart: added y-axis to top or bottom")
	}
	c.Options.Scales.YAxes = append(c.Options.Scales.YAxes, y)
	return y.ID, nil
}
<commit_msg>allow per-dataset float-format control<commit_after>// Package chartjs simplifies making chartjs.org plots in go.
package chartjs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"math"

	"github.com/brentp/go-chartjs/types"
)

var True = types.True
var False = types.False

var chartTypes = [...]string{
	"line",
	"bar",
	"bubble",
}

type chartType int

func (c chartType) MarshalJSON() ([]byte, error) {
	return []byte(`"` + chartTypes[c] + `"`), nil
}

const (
	// Line is a "line" plot
	Line chartType = iota
	// Bar is a "bar" plot
	Bar
	// Bubble is a "bubble" plot
	Bubble
)

type interpMode int

const (
	_ interpMode = iota
	InterpMonotone
	InterpDefault
)

var interpModes = [...]string{
	"",
	"monotone",
	"default",
}

func (m interpMode) MarshalJSON() ([]byte, error) {
	return []byte(`"` + interpModes[m] + `"`), nil
}

// XFloatFormat determines how many decimal places are sent in the JSON for X values.
var XFloatFormat = "%.2f"

// YFloatFormat determines how many decimal places are sent in the JSON for Y values.
var YFloatFormat = "%.2f"

// Values dictates the interface of data to be plotted.
type Values interface {
	// X-axis values. If only these are specified then it must be a Bar plot.
	Xs() []float64
	// Optional Y values.
	Ys() []float64
	// Rs are used to size points for chartType `Bubble`
	Rs() []float64
}

func marshalValuesJSON(v Values, xformat, yformat string) ([]byte, error) {
	xs, ys, rs := v.Xs(), v.Ys(), v.Rs()
	if len(xs) == 0 {
		if len(rs) != 0 {
			return nil, fmt.Errorf("chart: bad format of Values data")
		}
		xs = ys[:len(ys)]
		ys = nil
	}
	buf := bytes.NewBuffer(make([]byte, 0, 8*len(xs)))
	buf.WriteRune('[')
	if len(rs) > 0 {
		if len(xs) != len(ys) || len(xs) != len(rs) {
			return nil, fmt.Errorf("chart: bad format of Values. All axes must be of the same length")
		}
		var err error
		for i, x := range xs {
			if i > 0 {
				buf.WriteRune(',')
			}
			y, r := ys[i], rs[i]
			if math.IsNaN(y) {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + xformat + ",\"y\": null,\"r\":" + yformat + "}"), x, r))
			} else {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + xformat + ",\"y\":" + yformat + ",\"r\":" + yformat + "}"), x, y, r))
			}
			if err != nil {
				return nil, err
			}
		}
	} else if len(ys) > 0 {
		if len(xs) != len(ys) {
			return nil, fmt.Errorf("chart: bad format of Values. X and Y must be of the same length")
		}
		var err error
		for i, x := range xs {
			if i > 0 {
				buf.WriteRune(',')
			}
			y := ys[i]
			if math.IsNaN(y) {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + xformat + ",\"y\": null }"), x))
			} else {
				_, err = buf.WriteString(fmt.Sprintf(("{\"x\":" + xformat + ",\"y\":" + yformat + "}"), x, y))
			}
			if err != nil {
				return nil, err
			}
		}

	} else {
		for i, x := range xs {
			if i > 0 {
				buf.WriteRune(',')
			}
			_, err := buf.WriteString(fmt.Sprintf(xformat, x))
			if err != nil {
				return nil, err
			}
		}
	}

	buf.WriteRune(']')
	return buf.Bytes(), nil
}

// shape indicates the type of marker used for plotting.
type shape int

var shapes = []string{
	"",
	"circle",
	"triangle",
	"rect",
	"rectRot",
	"cross",
	"crossRot",
	"star",
	"line",
	"dash",
}

const (
	empty = iota
	Circle
	Triangle
	Rect
	RectRot
	Cross
	CrossRot
	Star
	LinePoint
	Dash
)

func (s shape) MarshalJSON() ([]byte, error) {
	return []byte(`"` + shapes[s] + `"`), nil
}

// Dataset wraps the "dataset" JSON
type Dataset struct {
	Data            Values      `json:"-"`
	Type            chartType   `json:"type,omitempty"`
	BackgroundColor *types.RGBA `json:"backgroundColor,omitempty"`
	// BorderColor is the color of the line.
	BorderColor *types.RGBA `json:"borderColor,omitempty"`
	// BorderWidth is the width of the line.
	BorderWidth float64 `json:"borderWidth"`

	// Label indicates the name of the dataset to be shown in the legend.
	Label string     `json:"label,omitempty"`
	Fill  types.Bool `json:"fill,omitempty"`

	// SteppedLine of true means dont interpolate and ignore line tension.
	SteppedLine            types.Bool  `json:"steppedLine,omitempty"`
	LineTension            float64     `json:"lineTension"`
	CubicInterpolationMode interpMode  `json:"cubicInterpolationMode,omitempty"`
	PointBackgroundColor   *types.RGBA `json:"pointBackgroundColor,omitempty"`
	PointBorderColor       *types.RGBA `json:"pointBorderColor,omitempty"`
	PointBorderWidth       float64     `json:"pointBorderWidth"`
	PointRadius            float64     `json:"pointRadius"`
	PointHitRadius         float64     `json:"pointHitRadius"`
	PointHoverRadius       float64     `json:"pointHoverRadius"`
	PointHoverBorderColor  *types.RGBA `json:"pointHoverBorderColor,omitempty"`
	PointHoverBorderWidth  float64     `json:"pointHoverBorderWidth"`
	PointStyle             shape       `json:"pointStyle,omitempty"`

	ShowLine types.Bool `json:"showLine,omitempty"`
	SpanGaps types.Bool `json:"spanGaps,omitempty"`

	// Axis ID that matches the ID on the Axis where this dataset is to be drawn.
	XAxisID string `json:"xAxisID,omitempty"`
	YAxisID string `json:"yAxisID,omitempty"`

	// set the formatter for the data, e.g. "%.2f"
	// these are not exported in the json, just used to determine the decimals of precision to show
	XFloatFormat string `json:"-"`
	YFloatFormat string `json:"-"`
}

// MarshalJSON implements json.Marshaler interface.
func (d Dataset) MarshalJSON() ([]byte, error) {
	xf, yf := d.XFloatFormat, d.YFloatFormat
	if xf == "" {
		xf = XFloatFormat
	}
	if yf == "" {
		yf = YFloatFormat
	}

	o, err := marshalValuesJSON(d.Data, xf, yf)
	// avoid recursion by creating an alias.
	type alias Dataset
	buf, err := json.Marshal(alias(d))
	if err != nil {
		return nil, err
	}
	// replace '}' with ',' to continue struct
	if len(buf) > 0 {
		buf[len(buf)-1] = ','
	}
	buf = append(buf, []byte(`"data":`)...)
	buf = append(buf, o...)
	buf = append(buf, '}')
	return buf, nil
}

// Data wraps the "data" JSON
type Data struct {
	Datasets []Dataset `json:"datasets"`
	Labels   []string  `json:"labels"`
}

type axisType int

var axisTypes = []string{
	"category",
	"linear",
	"logarithmic",
	"time",
	"radialLinear",
}

const (
	// Category is a categorical axis (this is the default),
	// used for bar plots.
	Category axisType = iota
	// Linear axis should be use for scatter plots.
	Linear
	// Log axis
	Log
	// Time axis
	Time
	// Radial axis
	Radial
)

func (t axisType) MarshalJSON() ([]byte, error) {
	return []byte("\"" + axisTypes[t] + "\""), nil
}

type axisPosition int

const (
	// Bottom puts the axis on the bottom (used for Y-axis)
	Bottom axisPosition = iota + 1
	// Top puts the axis on the bottom (used for Y-axis)
	Top
	// Left puts the axis on the bottom (used for X-axis)
	Left
	// Right puts the axis on the bottom (used for X-axis)
	Right
)

var axisPositions = []string{
	"",
	"bottom",
	"top",
	"left",
	"right",
}

func (p axisPosition) MarshalJSON() ([]byte, error) {
	return []byte(`"` + axisPositions[p] + `"`), nil
}

// Axis corresponds to 'scale' in chart.js lingo.
type Axis struct {
	Type      axisType     `json:"type"`
	Position  axisPosition `json:"position,omitempty"`
	Label     string       `json:"label,omitempty"`
	ID        string       `json:"id,omitempty"`
	GridLines types.Bool   `json:"gridLine,omitempty"`
	Stacked   types.Bool   `json:"stacked,omitempty"`

	// Bool differentiates between false and empty by use of pointer.
	Display    types.Bool  `json:"display,omitempty"`
	ScaleLabel *ScaleLabel `json:"scaleLabel,omitempty"`
	Tick       *Tick       `json:"ticks,omitempty"`
}

// Tick lets us set the range of the data.
type Tick struct {
	Min         float64    `json:"min,omitempty"`
	Max         float64    `json:"max,omitempty"`
	BeginAtZero types.Bool `json:"beginAtZero,omitempty"`
	// TODO: add additional options from: tick options.
}

// ScaleLabel corresponds to scale title.
// Display: True must be specified for this to be shown.
type ScaleLabel struct {
	Display     types.Bool  `json:"display,omitempty"`
	LabelString string      `json:"labelString,omitempty"`
	FontColor   *types.RGBA `json:"fontColor,omitempty"`
	FontFamily  string      `json:"fontFamily,omitempty"`
	FontSize    int         `json:"fontSize,omitempty"`
	FontStyle   string      `json:"fontStyle,omitempty"`
}

// Axes holds the X and Y axies. Its simpler to use Chart.AddXAxis, Chart.AddYAxis.
type Axes struct {
	XAxes []Axis `json:"xAxes,omitempty"`
	YAxes []Axis `json:"yAxes,omitempty"`
}

// AddX adds a X-Axis.
func (a *Axes) AddX(x Axis) {
	a.XAxes = append(a.XAxes, x)
}

// AddY adds a Y-Axis.
func (a *Axes) AddY(y Axis) {
	a.YAxes = append(a.YAxes, y)
}

// Option wraps the chartjs "option"
type Option struct {
	Responsive          types.Bool `json:"responsive,omitempty"`
	MaintainAspectRatio types.Bool `json:"maintainAspectRatio,omitempty"`
	Title               *Title     `json:"title,omitempty"`
}

// Title is the Options title
type Title struct {
	Display types.Bool `json:"display,omitempty"`
	Text    string     `json:"text,omitempty"`
}

// Options wraps the chartjs "options"
type Options struct {
	Option
	Scales  Axes     `json:"scales,omitempty"`
	Legend  *Legend  `json:"legend,omitempty"`
	Tooltip *Tooltip `json:"tooltips,omitempty"`
}

// Tooltip wraps chartjs "tooltips".
// TODO: figure out how to make this work.
type Tooltip struct {
	Enabled   types.Bool `json:"enabled,omitempty"`
	Intersect types.Bool `json:"intersect,omitempty"`
	// TODO: make mode typed by Interaction modes.
	Mode   string         `json:"mode,omitempty"`
	Custom template.JSStr `json:"custom,omitempty"`
}

type Legend struct {
	Display types.Bool `json:"display,omitempty"`
}

// Chart is the top-level type from chartjs.
type Chart struct {
	Type    chartType `json:"type"`
	Label   string    `json:"label,omitempty"`
	Data    Data      `json:"data,omitempty"`
	Options Options   `json:"options,omitempty"`
}

// AddDataset adds a dataset to the chart.
func (c *Chart) AddDataset(d Dataset) {
	c.Data.Datasets = append(c.Data.Datasets, d)
}

// AddXAxis adds an x-axis to the chart and returns the ID of the added axis.
func (c *Chart) AddXAxis(x Axis) (string, error) {
	if x.ID == "" {
		x.ID = fmt.Sprintf("xaxis%d", len(c.Options.Scales.XAxes))
	}
	if x.Position == Left || x.Position == Right {
		return "", fmt.Errorf("chart: added x-axis to left or right")
	}
	c.Options.Scales.XAxes = append(c.Options.Scales.XAxes, x)
	return x.ID, nil
}

// AddYAxis adds an y-axis to the chart and return the ID of the added axis.
func (c *Chart) AddYAxis(y Axis) (string, error) {
	if y.ID == "" {
		y.ID = fmt.Sprintf("yaxis%d", len(c.Options.Scales.YAxes))
	}
	if y.Position == Top || y.Position == Bottom {
		return "", fmt.Errorf("chart: added y-axis to top or bottom")
	}
	c.Options.Scales.YAxes = append(c.Options.Scales.YAxes, y)
	return y.ID, nil
}
<|endoftext|>


<commit_before>/**
This package is used to provide an implementation of the
Chord network protocol. It can be used to provide a DHT
which is tolerant to churn in the member ndoes.
*/
package chord

import (
	"crypto/sha1"
	"fmt"
	"hash"
	"time"
)

// Implements the methods needed for a Chord ring
type Transport interface {
	// Gets a list of the vnodes on the box
	ListVnodes(string) ([]*Vnode, error)

	// Ping a Vnode, check for liveness
	Ping(*Vnode) (bool, error)

	// Request a nodes predecessor
	GetPredecessor(*Vnode) (*Vnode, error)

	// Notify our successor of ourselves
	Notify(target, self *Vnode) ([]*Vnode, error)

	// Find a successor
	FindSuccessors(*Vnode, int, []byte) ([]*Vnode, error)

	// Clears a predecessor if it matches a given vnode. Used to leave.
	ClearPredecessor(target, self *Vnode) error

	// Instructs a node to skip a given successor. Used to leave.
	SkipSuccessor(target, self *Vnode) error

	// Register for an RPC callbacks
	Register(*Vnode, VnodeRPC)
}

// These are the methods to invoke on the registered vnodes
type VnodeRPC interface {
	GetPredecessor() (*Vnode, error)
	Notify(*Vnode) ([]*Vnode, error)
	FindSuccessors(int, []byte) ([]*Vnode, error)
	ClearPredecessor(*Vnode) error
	SkipSuccessor(*Vnode) error
}

// Delegate to notify on ring events
type Delegate interface {
	NewSuccessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	NewPredecessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	PredecessorLeaving(local *Vnode, remote *Vnode)
	SuccessorLeaving(local *Vnode, remote *Vnode)
}

// Configuration for Chord nodes
type Config struct {
	Hostname      string           // Local host name
	NumVnodes     int              // Number of vnodes per physical node
	HashFunc      func() hash.Hash // Hash function to use
	HashBits      int              // Bit size of the hash function
	StabilizeMin  time.Duration    // Minimum stabilization time
	StabilizeMax  time.Duration    // Maximum stabilization time
	NumSuccessors int              // Number of successors to maintain
	Delegate      Delegate         // Invoked to handle ring events
}

// Represents an Vnode, local or remote
type Vnode struct {
	Id   []byte // Virtual ID
	Host string // Host identifier
}

// Represents a local Vnode
type localVnode struct {
	Vnode
	ring        *Ring
	successors  []*Vnode
	finger      []*Vnode
	last_finger int
	predecessor *Vnode
	stabilized  time.Time
	timer       *time.Timer
}

// Stores the state required for a Chord ring
type Ring struct {
	config    *Config
	transport Transport
	vnodes    []*localVnode
	shutdown  chan bool
}

// Returns the default Ring configuration
func DefaultConfig(hostname string) *Config {
	return &Config{
		hostname,
		8,        // 8 vnodes
		sha1.New, // SHA1
		160,      // 160bit hash function
		time.Duration(15 * time.Second),
		time.Duration(45 * time.Second),
		8,   // 8 successors
		nil, // No delegate
	}
}

// Creates a new Chord ring given the config and transport
func Create(conf *Config, trans Transport) (*Ring, error) {
	// Create and initialize a ring
	ring := &Ring{}
	ring.init(conf, trans)
	ring.setLocalSuccessors()
	ring.schedule()
	return ring, nil
}

// Joins an existing Chord ring
func Join(conf *Config, trans Transport, existing string) (*Ring, error) {
	// Request a list of Vnodes from the remote host
	hosts, err := trans.ListVnodes(existing)
	if err != nil {
		return nil, err
	}
	if hosts == nil || len(hosts) == 0 {
		return nil, fmt.Errorf("Remote host has no vnodes!")
	}

	// Create a ring
	ring := &Ring{}
	ring.init(conf, trans)

	// Acquire a live successor for each Vnode
	for _, vn := range ring.vnodes {
		// Get the nearest remote vnode
		nearest := nearestVnodeToKey(hosts, vn.Id)

		// Query for a list of successors to this Vnode
		succs, err := trans.FindSuccessors(nearest, conf.NumSuccessors, vn.Id)
		if err != nil {
			return nil, fmt.Errorf("Failed to find successor for vnodes! Got %s", err)
		}
		if succs == nil || len(succs) == 0 {
			return nil, fmt.Errorf("Failed to find successor for vnodes! Got no vnodes!")
		}

		// Assign the successors
		for idx, s := range succs {
			vn.successors[idx] = s
		}
	}

	// Do a fast stabilization, will schedule regular execution
	for _, vn := range ring.vnodes {
		vn.stabilize()
	}
	return ring, nil
}

// Leaves a given Chord ring
func (*Ring) Leave() error {
	return nil
}

// Shutdown shuts down the local processes in a given Chord ring
// Blocks until all the vnodes terminate.
func (r *Ring) Shutdown() {
	// Wait for all the vnodes to shutdown
	r.shutdown = make(chan bool, r.config.NumVnodes)
	for i := 0; i < r.config.NumVnodes; i++ {
		<-r.shutdown
	}
}

// Does a key lookup for up to N successors of a key
func (r *Ring) Lookup(n int, key []byte) ([]*Vnode, error) {
	// Ensure that n is sane
	if n > r.config.NumSuccessors {
		return nil, fmt.Errorf("Cannot ask for more successors than NumSuccessors!")
	}

	// Hash the key
	h := r.config.HashFunc()
	h.Write(key)
	key_hash := h.Sum(nil)

	// Find the nearest local vnode
	nearest := r.nearestVnode(key_hash)

	// Use the nearest node for the lookup
	successors, err := nearest.FindSuccessors(n, key_hash)
	if err != nil {
		return nil, err
	}

	// Trim the nil successors
	for successors[len(successors)-1] == nil {
		successors = successors[:len(successors)-1]
	}
	return successors, nil
}
<commit_msg>Implement Leave<commit_after>/**
This package is used to provide an implementation of the
Chord network protocol. It can be used to provide a DHT
which is tolerant to churn in the member ndoes.
*/
package chord

import (
	"crypto/sha1"
	"fmt"
	"hash"
	"time"
)

// Implements the methods needed for a Chord ring
type Transport interface {
	// Gets a list of the vnodes on the box
	ListVnodes(string) ([]*Vnode, error)

	// Ping a Vnode, check for liveness
	Ping(*Vnode) (bool, error)

	// Request a nodes predecessor
	GetPredecessor(*Vnode) (*Vnode, error)

	// Notify our successor of ourselves
	Notify(target, self *Vnode) ([]*Vnode, error)

	// Find a successor
	FindSuccessors(*Vnode, int, []byte) ([]*Vnode, error)

	// Clears a predecessor if it matches a given vnode. Used to leave.
	ClearPredecessor(target, self *Vnode) error

	// Instructs a node to skip a given successor. Used to leave.
	SkipSuccessor(target, self *Vnode) error

	// Register for an RPC callbacks
	Register(*Vnode, VnodeRPC)
}

// These are the methods to invoke on the registered vnodes
type VnodeRPC interface {
	GetPredecessor() (*Vnode, error)
	Notify(*Vnode) ([]*Vnode, error)
	FindSuccessors(int, []byte) ([]*Vnode, error)
	ClearPredecessor(*Vnode) error
	SkipSuccessor(*Vnode) error
}

// Delegate to notify on ring events
type Delegate interface {
	NewSuccessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	NewPredecessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	PredecessorLeaving(local *Vnode, remote *Vnode)
	SuccessorLeaving(local *Vnode, remote *Vnode)
}

// Configuration for Chord nodes
type Config struct {
	Hostname      string           // Local host name
	NumVnodes     int              // Number of vnodes per physical node
	HashFunc      func() hash.Hash // Hash function to use
	HashBits      int              // Bit size of the hash function
	StabilizeMin  time.Duration    // Minimum stabilization time
	StabilizeMax  time.Duration    // Maximum stabilization time
	NumSuccessors int              // Number of successors to maintain
	Delegate      Delegate         // Invoked to handle ring events
}

// Represents an Vnode, local or remote
type Vnode struct {
	Id   []byte // Virtual ID
	Host string // Host identifier
}

// Represents a local Vnode
type localVnode struct {
	Vnode
	ring        *Ring
	successors  []*Vnode
	finger      []*Vnode
	last_finger int
	predecessor *Vnode
	stabilized  time.Time
	timer       *time.Timer
}

// Stores the state required for a Chord ring
type Ring struct {
	config    *Config
	transport Transport
	vnodes    []*localVnode
	shutdown  chan bool
}

// Returns the default Ring configuration
func DefaultConfig(hostname string) *Config {
	return &Config{
		hostname,
		8,        // 8 vnodes
		sha1.New, // SHA1
		160,      // 160bit hash function
		time.Duration(15 * time.Second),
		time.Duration(45 * time.Second),
		8,   // 8 successors
		nil, // No delegate
	}
}

// Creates a new Chord ring given the config and transport
func Create(conf *Config, trans Transport) (*Ring, error) {
	// Create and initialize a ring
	ring := &Ring{}
	ring.init(conf, trans)
	ring.setLocalSuccessors()
	ring.schedule()
	return ring, nil
}

// Joins an existing Chord ring
func Join(conf *Config, trans Transport, existing string) (*Ring, error) {
	// Request a list of Vnodes from the remote host
	hosts, err := trans.ListVnodes(existing)
	if err != nil {
		return nil, err
	}
	if hosts == nil || len(hosts) == 0 {
		return nil, fmt.Errorf("Remote host has no vnodes!")
	}

	// Create a ring
	ring := &Ring{}
	ring.init(conf, trans)

	// Acquire a live successor for each Vnode
	for _, vn := range ring.vnodes {
		// Get the nearest remote vnode
		nearest := nearestVnodeToKey(hosts, vn.Id)

		// Query for a list of successors to this Vnode
		succs, err := trans.FindSuccessors(nearest, conf.NumSuccessors, vn.Id)
		if err != nil {
			return nil, fmt.Errorf("Failed to find successor for vnodes! Got %s", err)
		}
		if succs == nil || len(succs) == 0 {
			return nil, fmt.Errorf("Failed to find successor for vnodes! Got no vnodes!")
		}

		// Assign the successors
		for idx, s := range succs {
			vn.successors[idx] = s
		}
	}

	// Do a fast stabilization, will schedule regular execution
	for _, vn := range ring.vnodes {
		vn.stabilize()
	}
	return ring, nil
}

// Leaves a given Chord ring and shuts down the local vnodes
func (r *Ring) Leave() error {
	// Shutdown the ring first to avoid further stabilization runs
	r.Shutdown()

	// Instruct each vnode to leave
	var err error
	for _, vn := range r.vnodes {
		err = mergeErrors(err, vn.leave())
	}
	return err
}

// Shutdown shuts down the local processes in a given Chord ring
// Blocks until all the vnodes terminate.
func (r *Ring) Shutdown() {
	// Wait for all the vnodes to shutdown
	r.shutdown = make(chan bool, r.config.NumVnodes)
	for i := 0; i < r.config.NumVnodes; i++ {
		<-r.shutdown
	}
}

// Does a key lookup for up to N successors of a key
func (r *Ring) Lookup(n int, key []byte) ([]*Vnode, error) {
	// Ensure that n is sane
	if n > r.config.NumSuccessors {
		return nil, fmt.Errorf("Cannot ask for more successors than NumSuccessors!")
	}

	// Hash the key
	h := r.config.HashFunc()
	h.Write(key)
	key_hash := h.Sum(nil)

	// Find the nearest local vnode
	nearest := r.nearestVnode(key_hash)

	// Use the nearest node for the lookup
	successors, err := nearest.FindSuccessors(n, key_hash)
	if err != nil {
		return nil, err
	}

	// Trim the nil successors
	for successors[len(successors)-1] == nil {
		successors = successors[:len(successors)-1]
	}
	return successors, nil
}
<|endoftext|>


<commit_before>package clock

import (
	"sort"
	"sync"
	"time"
)

// Clock represents an interface to the functions in the standard library time
// package. Two implementations are available in the clock package. The first
// is a real-time clock which simply wraps the time package's functions. The
// second is a mock clock which will only make forward progress when
// programmatically adjusted.
type Clock interface {
	After(d time.Duration) <-chan time.Time
	AfterFunc(d time.Duration, f func()) *Timer
	Now() time.Time
	Since(t time.Time) time.Duration
	Sleep(d time.Duration)
	Tick(d time.Duration) <-chan time.Time
	Ticker(d time.Duration) *Ticker
	Timer(d time.Duration) *Timer
}

// New returns an instance of a real-time clock.
func New() Clock {
	return &clock{}
}

// clock implements a real-time clock by simply wrapping the time package functions.
type clock struct{}

func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) }

func (c *clock) AfterFunc(d time.Duration, f func()) *Timer {
	return &Timer{timer: time.AfterFunc(d, f)}
}

func (c *clock) Now() time.Time { return time.Now() }

func (c *clock) Since(t time.Time) time.Duration { return time.Since(t) }

func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }

func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) }

func (c *clock) Ticker(d time.Duration) *Ticker {
	t := time.NewTicker(d)
	return &Ticker{C: t.C, ticker: t}
}

func (c *clock) Timer(d time.Duration) *Timer {
	t := time.NewTimer(d)
	return &Timer{C: t.C, timer: t}
}

// Mock represents a mock clock that only moves forward programmically.
// It can be preferable to a real-time clock when testing time-based functionality.
type Mock struct {
	mu     sync.Mutex
	now    time.Time   // current time
	timers clockTimers // tickers & timers
}

// NewMock returns an instance of a mock clock.
// The current time of the mock clock on initialization is the Unix epoch.
func NewMock() *Mock {
	return &Mock{now: time.Unix(0, 0)}
}

// Add moves the current time of the mock clock forward by the duration.
// This should only be called from a single goroutine at a time.
func (m *Mock) Add(d time.Duration) {
	// Calculate the final current time.
	t := m.now.Add(d)

	// Continue to execute timers until there are no more before the new time.
	for {
		if !m.runNextTimer(t) {
			break
		}
	}

	// Ensure that we end with the new time.
	m.mu.Lock()
	m.now = t
	m.mu.Unlock()

	// Give a small buffer to make sure the other goroutines get handled.
	gosched()
}

// Set sets the current time of the mock clock to a specific one.
// This should only be called from a single goroutine at a time.
func (m *Mock) Set(t time.Time) {
	// Continue to execute timers until there are no more before the new time.
	for {
		if !m.runNextTimer(t) {
			break
		}
	}

	// Ensure that we end with the new time.
	m.mu.Lock()
	m.now = t
	m.mu.Unlock()

	// Give a small buffer to make sure the other goroutines get handled.
	gosched()
}

// runNextTimer executes the next timer in chronological order and moves the
// current time to the timer's next tick time. The next time is not executed if
// it's next time if after the max time. Returns true if a timer is executed.
func (m *Mock) runNextTimer(max time.Time) bool {
	m.mu.Lock()

	// Sort timers by time.
	sort.Sort(m.timers)

	// If we have no more timers then exit.
	if len(m.timers) == 0 {
		m.mu.Unlock()
		return false
	}

	// Retrieve next timer. Exit if next tick is after new time.
	t := m.timers[0]
	if t.Next().After(max) {
		m.mu.Unlock()
		return false
	}

	// Move "now" forward and unlock clock.
	m.now = t.Next()
	m.mu.Unlock()

	// Execute timer.
	t.Tick(m.now)
	return true
}

// After waits for the duration to elapse and then sends the current time on the returned channel.
func (m *Mock) After(d time.Duration) <-chan time.Time {
	return m.Timer(d).C
}

// AfterFunc waits for the duration to elapse and then executes a function.
// A Timer is returned that can be stopped.
func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
	t := m.Timer(d)
	t.C = nil
	t.fn = f
	return t
}

// Now returns the current wall time on the mock clock.
func (m *Mock) Now() time.Time {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.now
}

// Since returns time since the mock clocks wall time.
func (m *Mock) Since(t time.Time) time.Duration {
	return m.Now().Sub(t)
}

// Sleep pauses the goroutine for the given duration on the mock clock.
// The clock must be moved forward in a separate goroutine.
func (m *Mock) Sleep(d time.Duration) {
	<-m.After(d)
}

// Tick is a convenience function for Ticker().
// It will return a ticker channel that cannot be stopped.
func (m *Mock) Tick(d time.Duration) <-chan time.Time {
	return m.Ticker(d).C
}

// Ticker creates a new instance of Ticker.
func (m *Mock) Ticker(d time.Duration) *Ticker {
	m.mu.Lock()
	defer m.mu.Unlock()
	ch := make(chan time.Time, 1)
	t := &Ticker{
		C:    ch,
		c:    ch,
		mock: m,
		d:    d,
		next: m.now.Add(d),
	}
	m.timers = append(m.timers, (*internalTicker)(t))
	return t
}

// Timer creates a new instance of Timer.
func (m *Mock) Timer(d time.Duration) *Timer {
	m.mu.Lock()
	defer m.mu.Unlock()
	ch := make(chan time.Time, 1)
	t := &Timer{
		C:       ch,
		c:       ch,
		mock:    m,
		next:    m.now.Add(d),
		stopped: false,
	}
	m.timers = append(m.timers, (*internalTimer)(t))
	return t
}

func (m *Mock) removeClockTimer(t clockTimer) {
	m.mu.Lock()
	defer m.mu.Unlock()
	for i, timer := range m.timers {
		if timer == t {
			copy(m.timers[i:], m.timers[i+1:])
			m.timers[len(m.timers)-1] = nil
			m.timers = m.timers[:len(m.timers)-1]
			break
		}
	}
	sort.Sort(m.timers)
}

// clockTimer represents an object with an associated start time.
type clockTimer interface {
	Next() time.Time
	Tick(time.Time)
}

// clockTimers represents a list of sortable timers.
type clockTimers []clockTimer

func (a clockTimers) Len() int           { return len(a) }
func (a clockTimers) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) }

// Timer represents a single event.
// The current time will be sent on C, unless the timer was created by AfterFunc.
type Timer struct {
	C       <-chan time.Time
	c       chan time.Time
	timer   *time.Timer // realtime impl, if set
	next    time.Time   // next tick time
	mock    *Mock       // mock clock, if set
	fn      func()      // AfterFunc function, if set
	stopped bool        // True if stopped, false if running
}

// Stop turns off the ticker.
func (t *Timer) Stop() bool {
	if t.timer != nil {
		return t.timer.Stop()
	}

	t.mock.mu.Lock()
	registered := !t.stopped
	t.mock.mu.Unlock()

	t.mock.removeClockTimer((*internalTimer)(t))
	t.mock.mu.Lock()
	defer t.mock.mu.Unlock()
	t.stopped = true
	return registered
}

// Reset changes the expiry time of the timer
func (t *Timer) Reset(d time.Duration) bool {
	if t.timer != nil {
		return t.timer.Reset(d)
	}

	t.next = t.mock.now.Add(d)
	t.mock.mu.Lock()
	defer t.mock.mu.Unlock()

	registered := !t.stopped
	if t.stopped {
		t.mock.timers = append(t.mock.timers, (*internalTimer)(t))
	}

	t.stopped = false
	return registered
}

type internalTimer Timer

func (t *internalTimer) Next() time.Time { return t.next }
func (t *internalTimer) Tick(now time.Time) {
	if t.fn != nil {
		t.fn()
	} else {
		t.c <- now
	}
	t.mock.removeClockTimer((*internalTimer)(t))
	t.mock.mu.Lock()
	defer t.mock.mu.Unlock()

	t.stopped = true
	gosched()
}

// Ticker holds a channel that receives "ticks" at regular intervals.
type Ticker struct {
	C      <-chan time.Time
	c      chan time.Time
	ticker *time.Ticker  // realtime impl, if set
	next   time.Time     // next tick time
	mock   *Mock         // mock clock, if set
	d      time.Duration // time between ticks
}

// Stop turns off the ticker.
func (t *Ticker) Stop() {
	if t.ticker != nil {
		t.ticker.Stop()
	} else {
		t.mock.removeClockTimer((*internalTicker)(t))
	}
}

type internalTicker Ticker

func (t *internalTicker) Next() time.Time { return t.next }
func (t *internalTicker) Tick(now time.Time) {
	select {
	case t.c <- now:
	default:
	}
	t.next = now.Add(t.d)
	gosched()
}

// Sleep momentarily so that other goroutines can process.
func gosched() { time.Sleep(1 * time.Millisecond) }
<commit_msg>Grammar: clock.go<commit_after>package clock

import (
	"sort"
	"sync"
	"time"
)

// Clock represents an interface to the functions in the standard library time
// package. Two implementations are available in the clock package. The first
// is a real-time clock which simply wraps the time package's functions. The
// second is a mock clock which will only change when
// programmatically adjusted.
type Clock interface {
	After(d time.Duration) <-chan time.Time
	AfterFunc(d time.Duration, f func()) *Timer
	Now() time.Time
	Since(t time.Time) time.Duration
	Sleep(d time.Duration)
	Tick(d time.Duration) <-chan time.Time
	Ticker(d time.Duration) *Ticker
	Timer(d time.Duration) *Timer
}

// New returns an instance of a real-time clock.
func New() Clock {
	return &clock{}
}

// clock implements a real-time clock by simply wrapping the time package functions.
type clock struct{}

func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) }

func (c *clock) AfterFunc(d time.Duration, f func()) *Timer {
	return &Timer{timer: time.AfterFunc(d, f)}
}

func (c *clock) Now() time.Time { return time.Now() }

func (c *clock) Since(t time.Time) time.Duration { return time.Since(t) }

func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }

func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) }

func (c *clock) Ticker(d time.Duration) *Ticker {
	t := time.NewTicker(d)
	return &Ticker{C: t.C, ticker: t}
}

func (c *clock) Timer(d time.Duration) *Timer {
	t := time.NewTimer(d)
	return &Timer{C: t.C, timer: t}
}

// Mock represents a mock clock that only moves forward programmatically.
// It can be preferable to a real-time clock when testing time-based functionality.
type Mock struct {
	mu     sync.Mutex
	now    time.Time   // current time
	timers clockTimers // tickers & timers
}

// NewMock returns an instance of a mock clock.
// The current time of the mock clock on initialization is the Unix epoch.
func NewMock() *Mock {
	return &Mock{now: time.Unix(0, 0)}
}

// Add moves the current time of the mock clock forward by the specified duration.
// This should only be called from a single goroutine at a time.
func (m *Mock) Add(d time.Duration) {
	// Calculate the final current time.
	t := m.now.Add(d)

	// Continue to execute timers until there are no more before the new time.
	for {
		if !m.runNextTimer(t) {
			break
		}
	}

	// Ensure that we end with the new time.
	m.mu.Lock()
	m.now = t
	m.mu.Unlock()

	// Give a small buffer to make sure that other goroutines get handled.
	gosched()
}

// Set sets the current time of the mock clock to a specific one.
// This should only be called from a single goroutine at a time.
func (m *Mock) Set(t time.Time) {
	// Continue to execute timers until there are no more before the new time.
	for {
		if !m.runNextTimer(t) {
			break
		}
	}

	// Ensure that we end with the new time.
	m.mu.Lock()
	m.now = t
	m.mu.Unlock()

	// Give a small buffer to make sure that other goroutines get handled.
	gosched()
}

// runNextTimer executes the next timer in chronological order and moves the
// current time to the timer's next tick time. The next time is not executed if
// its next time is after the max time. Returns true if a timer was executed.
func (m *Mock) runNextTimer(max time.Time) bool {
	m.mu.Lock()

	// Sort timers by time.
	sort.Sort(m.timers)

	// If we have no more timers then exit.
	if len(m.timers) == 0 {
		m.mu.Unlock()
		return false
	}

	// Retrieve next timer. Exit if next tick is after new time.
	t := m.timers[0]
	if t.Next().After(max) {
		m.mu.Unlock()
		return false
	}

	// Move "now" forward and unlock clock.
	m.now = t.Next()
	m.mu.Unlock()

	// Execute timer.
	t.Tick(m.now)
	return true
}

// After waits for the duration to elapse and then sends the current time on the returned channel.
func (m *Mock) After(d time.Duration) <-chan time.Time {
	return m.Timer(d).C
}

// AfterFunc waits for the duration to elapse and then executes a function.
// A Timer is returned that can be stopped.
func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
	t := m.Timer(d)
	t.C = nil
	t.fn = f
	return t
}

// Now returns the current wall time on the mock clock.
func (m *Mock) Now() time.Time {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.now
}

// Since returns time since the mock clock's wall time.
func (m *Mock) Since(t time.Time) time.Duration {
	return m.Now().Sub(t)
}

// Sleep pauses the goroutine for the given duration on the mock clock.
// The clock must be moved forward in a separate goroutine.
func (m *Mock) Sleep(d time.Duration) {
	<-m.After(d)
}

// Tick is a convenience function for Ticker().
// It will return a ticker channel that cannot be stopped.
func (m *Mock) Tick(d time.Duration) <-chan time.Time {
	return m.Ticker(d).C
}

// Ticker creates a new instance of Ticker.
func (m *Mock) Ticker(d time.Duration) *Ticker {
	m.mu.Lock()
	defer m.mu.Unlock()
	ch := make(chan time.Time, 1)
	t := &Ticker{
		C:    ch,
		c:    ch,
		mock: m,
		d:    d,
		next: m.now.Add(d),
	}
	m.timers = append(m.timers, (*internalTicker)(t))
	return t
}

// Timer creates a new instance of Timer.
func (m *Mock) Timer(d time.Duration) *Timer {
	m.mu.Lock()
	defer m.mu.Unlock()
	ch := make(chan time.Time, 1)
	t := &Timer{
		C:       ch,
		c:       ch,
		mock:    m,
		next:    m.now.Add(d),
		stopped: false,
	}
	m.timers = append(m.timers, (*internalTimer)(t))
	return t
}

func (m *Mock) removeClockTimer(t clockTimer) {
	m.mu.Lock()
	defer m.mu.Unlock()
	for i, timer := range m.timers {
		if timer == t {
			copy(m.timers[i:], m.timers[i+1:])
			m.timers[len(m.timers)-1] = nil
			m.timers = m.timers[:len(m.timers)-1]
			break
		}
	}
	sort.Sort(m.timers)
}

// clockTimer represents an object with an associated start time.
type clockTimer interface {
	Next() time.Time
	Tick(time.Time)
}

// clockTimers represents a list of sortable timers.
type clockTimers []clockTimer

func (a clockTimers) Len() int           { return len(a) }
func (a clockTimers) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) }

// Timer represents a single event.
// The current time will be sent on C, unless the timer was created by AfterFunc.
type Timer struct {
	C       <-chan time.Time
	c       chan time.Time
	timer   *time.Timer // realtime impl, if set
	next    time.Time   // next tick time
	mock    *Mock       // mock clock, if set
	fn      func()      // AfterFunc function, if set
	stopped bool        // True if stopped, false if running
}

// Stop turns off the ticker.
func (t *Timer) Stop() bool {
	if t.timer != nil {
		return t.timer.Stop()
	}

	t.mock.mu.Lock()
	registered := !t.stopped
	t.mock.mu.Unlock()

	t.mock.removeClockTimer((*internalTimer)(t))
	t.mock.mu.Lock()
	defer t.mock.mu.Unlock()
	t.stopped = true
	return registered
}

// Reset changes the expiry time of the timer
func (t *Timer) Reset(d time.Duration) bool {
	if t.timer != nil {
		return t.timer.Reset(d)
	}

	t.next = t.mock.now.Add(d)
	t.mock.mu.Lock()
	defer t.mock.mu.Unlock()

	registered := !t.stopped
	if t.stopped {
		t.mock.timers = append(t.mock.timers, (*internalTimer)(t))
	}

	t.stopped = false
	return registered
}

type internalTimer Timer

func (t *internalTimer) Next() time.Time { return t.next }
func (t *internalTimer) Tick(now time.Time) {
	if t.fn != nil {
		t.fn()
	} else {
		t.c <- now
	}
	t.mock.removeClockTimer((*internalTimer)(t))
	t.mock.mu.Lock()
	defer t.mock.mu.Unlock()

	t.stopped = true
	gosched()
}

// Ticker holds a channel that receives "ticks" at regular intervals.
type Ticker struct {
	C      <-chan time.Time
	c      chan time.Time
	ticker *time.Ticker  // realtime impl, if set
	next   time.Time     // next tick time
	mock   *Mock         // mock clock, if set
	d      time.Duration // time between ticks
}

// Stop turns off the ticker.
func (t *Ticker) Stop() {
	if t.ticker != nil {
		t.ticker.Stop()
	} else {
		t.mock.removeClockTimer((*internalTicker)(t))
	}
}

type internalTicker Ticker

func (t *internalTicker) Next() time.Time { return t.next }
func (t *internalTicker) Tick(now time.Time) {
	select {
	case t.c <- now:
	default:
	}
	t.next = now.Add(t.d)
	gosched()
}

// Sleep momentarily so that other goroutines can process.
func gosched() { time.Sleep(1 * time.Millisecond) }
<|endoftext|>


<commit_before>package renter

import (
	"errors"
	"os"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/consensus"
	"github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

var (
	ErrNilCS     = errors.New("cannot create renter with nil consensus set")
	ErrNilHostDB = errors.New("cannot create renter with nil hostdb")
	ErrNilWallet = errors.New("cannot create renter wil nil wlalet")
)

// A Renter is responsible for tracking all of the files that a user has
// uploaded to Sia, as well as the locations and health of these files.
type Renter struct {
	cs          *consensus.State
	hostDB      modules.HostDB
	wallet      modules.Wallet
	blockHeight types.BlockHeight

	files         map[string]*file
	downloadQueue []*Download
	saveDir       string

	subscriptions []chan struct{}

	mu *sync.RWMutex
}

// New returns an empty renter.
func New(cs *consensus.State, hdb modules.HostDB, wallet modules.Wallet, saveDir string) (*Renter, error) {
	if cs == nil {
		return nil, ErrNilCS
	}
	if hdb == nil {
		return nil, ErrNilHostDB
	}
	if wallet == nil {
		return nil, ErrNilWallet
	}

	r := &Renter{
		cs:     cs,
		hostDB: hdb,
		wallet: wallet,

		files:   make(map[string]*file),
		saveDir: saveDir,

		mu: sync.New(modules.SafeMutexDelay, 1),
	}

	err := os.MkdirAll(saveDir, 0700)
	if err != nil {
		return nil, err
	}

	r.load()

	// TODO: I'm worried about balances here. Because of the way that the
	// re-try algorithm works, it won't be a problem, but without that we would
	// need to make sure that scanAllFiles() didn't get called until the entire
	// balance had loaded, which would require loading the entire blockchain.
	// This also won't be a problem once we're also saving the addresses.
	//
	// TODO: bring back this functionality when we have resumable uploads.
	//r.scanAllFiles()

	r.cs.ConsensusSetSubscribe(r)

	return r, nil
}

// Info returns generic information about the renter and the files that are
// being rented.
func (r *Renter) Info() (ri modules.RentInfo) {
	lockID := r.mu.RLock()
	defer r.mu.RUnlock(lockID)

	// Include the list of files the renter knows about.
	for filename := range r.files {
		ri.Files = append(ri.Files, filename)
	}

	// Calculate the average cost of a file.
	var averagePrice types.Currency
	sampleSize := redundancy * 2
	hosts := r.hostDB.RandomHosts(sampleSize)
	for _, host := range hosts {
		averagePrice = averagePrice.Add(host.Price)
	}
	averagePrice = averagePrice.Div(types.NewCurrency64(uint64(len(hosts))))
	estimatedCost := averagePrice.Mul(types.NewCurrency64(6000)).Mul(types.NewCurrency64(1024 * 1024 * 1024))
	bufferedCost := estimatedCost.Mul(types.NewCurrency64(2))
	ri.Price = bufferedCost

	return
}
<commit_msg>comment hacky price estimator<commit_after>package renter

import (
	"errors"
	"os"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/consensus"
	"github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

var (
	ErrNilCS     = errors.New("cannot create renter with nil consensus set")
	ErrNilHostDB = errors.New("cannot create renter with nil hostdb")
	ErrNilWallet = errors.New("cannot create renter wil nil wlalet")
)

// A Renter is responsible for tracking all of the files that a user has
// uploaded to Sia, as well as the locations and health of these files.
type Renter struct {
	cs          *consensus.State
	hostDB      modules.HostDB
	wallet      modules.Wallet
	blockHeight types.BlockHeight

	files         map[string]*file
	downloadQueue []*Download
	saveDir       string

	subscriptions []chan struct{}

	mu *sync.RWMutex
}

// New returns an empty renter.
func New(cs *consensus.State, hdb modules.HostDB, wallet modules.Wallet, saveDir string) (*Renter, error) {
	if cs == nil {
		return nil, ErrNilCS
	}
	if hdb == nil {
		return nil, ErrNilHostDB
	}
	if wallet == nil {
		return nil, ErrNilWallet
	}

	r := &Renter{
		cs:     cs,
		hostDB: hdb,
		wallet: wallet,

		files:   make(map[string]*file),
		saveDir: saveDir,

		mu: sync.New(modules.SafeMutexDelay, 1),
	}

	err := os.MkdirAll(saveDir, 0700)
	if err != nil {
		return nil, err
	}

	r.load()

	// TODO: I'm worried about balances here. Because of the way that the
	// re-try algorithm works, it won't be a problem, but without that we would
	// need to make sure that scanAllFiles() didn't get called until the entire
	// balance had loaded, which would require loading the entire blockchain.
	// This also won't be a problem once we're also saving the addresses.
	//
	// TODO: bring back this functionality when we have resumable uploads.
	//r.scanAllFiles()

	r.cs.ConsensusSetSubscribe(r)

	return r, nil
}

// Info returns generic information about the renter and the files that are
// being rented.
func (r *Renter) Info() (ri modules.RentInfo) {
	lockID := r.mu.RLock()
	defer r.mu.RUnlock(lockID)

	// Include the list of files the renter knows about.
	for filename := range r.files {
		ri.Files = append(ri.Files, filename)
	}

	// Calculate the average cost of a file.
	var averagePrice types.Currency
	sampleSize := redundancy * 2
	hosts := r.hostDB.RandomHosts(sampleSize)
	for _, host := range hosts {
		averagePrice = averagePrice.Add(host.Price)
	}
	averagePrice = averagePrice.Div(types.NewCurrency64(uint64(len(hosts))))
	// HACK: 6000 is the duration (set by the API), and 1024^3 is a GB. Price
	// is reported as per GB, no timeframe is given.
	estimatedCost := averagePrice.Mul(types.NewCurrency64(6000)).Mul(types.NewCurrency64(1024 * 1024 * 1024))
	bufferedCost := estimatedCost.Mul(types.NewCurrency64(2))
	ri.Price = bufferedCost

	return
}
<|endoftext|>