<commit_before>package config

import (
	"fmt"
	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
	"log"
	"net/url"
	"strings"
	"time"
)

type EtcdOptions struct {
	Scheme string        `long:"etcd-scheme" value-name:"http|https" default:"http" description:"Set default scheme for etcd:// URLs"`
	Hosts  []string      `long:"etcd-host" value-name:"HOST:PORT" description:"Include hosts"`
	Prefix string        `long:"etcd-prefix" value-name:"/PATH" default:"/clusterf" description:"Namespace all keys under given path"`
	TTL    time.Duration `long:"etcd-ttl" value-name:"DURATION" default:"10s" description:"Write values with given TTL, and refresh at half of that"`
}

func (options EtcdOptions) OpenURL(url *url.URL) (*EtcdSource, error) {
	switch url.Scheme {
	case "etcd":

	case "etcd+http":
		options.Scheme = "http"
	case "etcd+https":
		options.Scheme = "https"
	}

	for _, host := range strings.Split(url.Host, ",") {
		options.Hosts = append(options.Hosts, host)
	}

	if url.Path != "" {
		options.Prefix = url.Path
	}

	return options.Open()
}

func (options EtcdOptions) String() string {
	return fmt.Sprintf("etcd+%s://%s%s", options.Scheme, strings.Join(options.Hosts, ","), options.Prefix)
}

func (options EtcdOptions) clientConfig() (clientConfig client.Config, err error) {
	for _, host := range options.Hosts {
		endpointURL := url.URL{Scheme: options.Scheme, Host: host}

		clientConfig.Endpoints = append(clientConfig.Endpoints, endpointURL.String())
	}

	return
}

func (options EtcdOptions) Open() (*EtcdSource, error) {
	etcdSource := EtcdSource{
		options: options,
	}

	if clientConfig, err := options.clientConfig(); err != nil {
		return nil, err
	} else if client, err := client.New(clientConfig); err != nil {
		return nil, err
	} else {
		etcdSource.client = client
	}

	etcdSource.keysAPI = client.NewKeysAPI(etcdSource.client)

	return &etcdSource, nil
}

// Undo etcd/client:ClusterError fuckery
// https://github.com/coreos/etcd/pull/4503
func fixupClusterError(err error) error {
	if clusterError, ok := err.(*client.ClusterError); ok {
		var errs []string

		for _, clusterErr := range clusterError.Errors {
			errs = append(errs, clusterErr.Error())
		}

		return fmt.Errorf("%s: %s", clusterError.Error(), strings.Join(errs, "; "))
	} else {
		return err
	}
}

type EtcdSource struct {
	options EtcdOptions

	client  client.Client
	keysAPI client.KeysAPI

	// state to track changes from Scan() to Sync()
	syncIndex uint64

	// refresh nodes
	writeChan chan map[string]Node
	flushChan chan error
}

func (etcd *EtcdSource) String() string {
	return etcd.options.String()
}

func (etcd *EtcdSource) path(parts ...string) string {
	return strings.Join(append([]string{etcd.options.Prefix}, parts...), "/")
}

/*
 * Initialize state in etcd.
 *
 * Creates the top-level config directory if it does not exist, and initialize to follow it
 */
func (etcd *EtcdSource) Init() error {
	if response, err := etcd.keysAPI.Set(context.Background(), etcd.path(), "", &client.SetOptions{Dir: true}); err != nil {
		return fixupClusterError(err)
	} else {
		etcd.syncIndex = response.Node.CreatedIndex
	}

	return nil
}

/*
 * Synchronize current state in etcd.
 *
 * Does a recursive get on the complete /clusterf tree in etcd, and builds the services state from it.
 *
 * Stores the current etcd-index from the snapshot in .syncIndex, so that .Sync() can be used to continue updating any changes.
 */
func (etcd *EtcdSource) Scan() ([]Node, error) {
	response, err := etcd.keysAPI.Get(context.Background(), etcd.path(), &client.GetOptions{Recursive: true})

	if err == nil {

	} else if clientError, ok := err.(client.Error); ok && clientError.Code == client.ErrorCodeKeyNotFound {
		// create directory instead
		return nil, etcd.Init()
	} else {
		return nil, fixupClusterError(err)
	}

	if response.Node.Dir != true {
		return nil, fmt.Errorf("etcd prefix=%s is not a directory", response.Node.Key)
	}

	// the tree root's ModifiedTime may be a long long time in the past, so we can't want to use that for waits
	// we assume this enough to ensure atomic sync with .Watch() on the same tree..
	etcd.syncIndex = response.Index

	// scan, collect and return
	var nodes []Node

	err = etcd.scanNode(response.Node, func(node Node) { nodes = append(nodes, node) })

	return nodes, err
}

func (etcd *EtcdSource) parseNode(etcdNode *client.Node) (node Node, err error) {
	// decode etcd path into config tree path
	path := etcdNode.Key

	if !strings.HasPrefix(path, etcd.options.Prefix) {
		return node, fmt.Errorf("node outside tree: %s", path)
	}

	path = strings.TrimPrefix(path, etcd.options.Prefix)
	path = strings.Trim(path, "/")

	node.Source = etcd
	node.Path = path
	node.IsDir = etcdNode.Dir
	node.Value = etcdNode.Value

	return
}

// Scan through the recursive /clusterf node to return Nodes in pre-order (top-level nodes before their children)
func (etcd *EtcdSource) scanNode(etcdNode *client.Node, handler func(node Node)) error {
	if node, err := etcd.parseNode(etcdNode); err != nil {
		return err
	} else {
		// pre-order
		handler(node)
	}

	// recurse
	for _, childNode := range etcdNode.Nodes {
		if err := etcd.scanNode(childNode, handler); err != nil {
			return err
		}
	}

	return nil
}

/*
 * Watch for changed Nodes in etcd.
 *
 * Sends any changes on the returned channel. Closes the channel on errors.
 */
func (etcd *EtcdSource) Sync(syncChan chan Node) error {
	// kick off new goroutine to handle initial services and updates
	go etcd.watch(syncChan)

	return nil
}

// Watch etcd for changes, and sync them over the chan
func (etcd *EtcdSource) watch(watchChan chan Node) {
	defer close(watchChan)

	watcher := etcd.keysAPI.Watcher(etcd.path(), &client.WatcherOptions{AfterIndex: etcd.syncIndex, Recursive: true})

	for {
		if response, err := watcher.Next(context.Background()); err != nil {
			err = fixupClusterError(err)
			log.Printf("config:EtcdSource.watch: %v", err)
			return
		} else if node, err := etcd.syncNode(response.Action, response.Node); err != nil {
			log.Printf("config:EtcdSource.watch %#v: syncNode: %s", response, err)
			return
		} else {
			log.Printf("config:EtcdSource.watch: %v %v", response.Action, node)
			watchChan <- node
		}
	}
}

// Handle changed node
func (etcd *EtcdSource) syncNode(etcdAction string, etcdNode *client.Node) (Node, error) {
	node, err := etcd.parseNode(etcdNode)
	if err != nil {
		return node, err
	}

	// decode action
	switch etcdAction {
	case "create", "set", "update", "compareAndSwap":

	case "delete", "expire", "compareAndDelete":
		node.Remove = true

	default:
		return node, fmt.Errorf("Unknown etcd action: %s", etcdAction)
	}

	return node, nil
}

// Refresh TTL for existing key that has not yet expired
func (etcd *EtcdSource) refresh(node Node) error {
	var opts = client.SetOptions{
		TTL:     etcd.options.TTL,
		Refresh: true,
	}

	if _, err := etcd.keysAPI.Set(context.Background(), etcd.path(node.Path), "", &opts); err != nil {
		return fixupClusterError(err)
	} else {
		return nil
	}
}

func (etcd *EtcdSource) set(node Node) error {
	var opts = client.SetOptions{
		TTL: etcd.options.TTL,
		Dir: node.IsDir,
	}

	if _, err := etcd.keysAPI.Set(context.Background(), etcd.path(node.Path), node.Value, &opts); err != nil {
		return fixupClusterError(err)
	} else {
		return nil
	}
}

func (etcd *EtcdSource) remove(node Node) error {
	var opts = client.DeleteOptions{
		Dir: node.IsDir,
	}

	if node.IsDir {
		opts.Recursive = true
	}

	if _, err := etcd.keysAPI.Delete(context.Background(), etcd.path(node.Path), &opts); err != nil {
		return fixupClusterError(err)
	} else {
		return nil
	}
}

func (etcd *EtcdSource) writer() {
	defer close(etcd.flushChan)

	var nodes map[string]Node
	var timer = time.Tick(etcd.options.TTL / 2)
	var loopStart, loopEnd time.Time

	for {
		// XXX: how much of our TTL does this write-loop consume...?
		select {
		case refreshTime := <-timer:
			if refreshTime.Before(loopEnd) {
				log.Printf("config:EtcdSource %v: late refresh at %v-%v", etcd, loopEnd.Sub(loopStart), loopEnd.Sub(refreshTime))
			} else {
				log.Printf("config:EtcdSource %v: refresh after %v+%v", etcd, loopEnd.Sub(loopStart), refreshTime.Sub(loopEnd))
			}

			loopStart = time.Now()

			// what happens if we're slow, and our TTLs expire before we can refresh?
			// refresh will probably fail, and we should recover..
			for _, node := range nodes {
				if err := etcd.refresh(node); err != nil {
					log.Printf("config:EtcdSource %v: writer: refresh %v: %v", etcd, node, err)
				}
			}

			loopEnd = time.Now()

		case writeNodes, open := <-etcd.writeChan:
			loopStart = time.Now()

			// update to new dict
			// if the chan is closed from Flush(), this will get an empty map - and we remove all nodes
			for key, node := range nodes {
				if _, exists := writeNodes[key]; !exists {
					// removed
					if err := etcd.remove(node); err != nil {
						log.Printf("config:EtcdSource %v: writer: remove %v: %v", etcd, node, err)
					} else {
						log.Printf("config:EtcdSource %v: writer: remove %v", etcd, node)
					}
				}
			}
			for key, node := range writeNodes {
				if oldNode, exists := nodes[key]; !exists {
					// new node
					if err := etcd.set(node); err != nil {
						log.Printf("config:EtcdSource %v: writer: new %v: %v", etcd, node, err)
					} else {
						log.Printf("config:EtcdSource %v: writer: new %v", etcd, node)
					}
				} else if !node.Equals(oldNode) {
					// changed
					if err := etcd.set(node); err != nil {
						log.Printf("config:EtcdSource %v: writer: set %v: %v", etcd, node, err)
					} else {
						log.Printf("config:EtcdSource %v: writer: set %v", etcd, node)
					}
				}
			}

			if open {
				// XXX: update in place when actions are succesful instead?
				nodes = writeNodes
			} else {
				// exit
				return
			}

			loopEnd = time.Now()

			log.Printf("config:EtcdSource %v: writer: update in %v", etcd, loopEnd.Sub(loopStart))
		}

	}
}

// Publish a config into etcd. The node will be refreshed per our TTL
func (etcd *EtcdSource) Write(nodes map[string]Node) error {
	if etcd.writeChan == nil {
		etcd.writeChan = make(chan map[string]Node)
		etcd.flushChan = make(chan error)

		go etcd.writer()
	}

	etcd.writeChan <- nodes

	// XXX: errors?
	return nil
}

// Remove all published nodes
func (etcd *EtcdSource) Flush() (err error) {
	if etcd.writeChan != nil {
		close(etcd.writeChan)
	}

	// wait for flush to complete
	for err = range etcd.flushChan {
		log.Printf("config:EtcdSource %v: Flush: %v", etcd, err)
	}

	return
}
<commit_msg>config/etcd: writer fallback to set() if refresh() fails<commit_after>package config

import (
	"fmt"
	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
	"log"
	"math/rand"
	"net/url"
	"strings"
	"time"
)

type EtcdOptions struct {
	Scheme string        `long:"etcd-scheme" value-name:"http|https" default:"http" description:"Set default scheme for etcd:// URLs"`
	Hosts  []string      `long:"etcd-host" value-name:"HOST:PORT" description:"Include hosts"`
	Prefix string        `long:"etcd-prefix" value-name:"/PATH" default:"/clusterf" description:"Namespace all keys under given path"`
	TTL    time.Duration `long:"etcd-ttl" value-name:"DURATION" default:"10s" description:"Write values with given TTL, and refresh at half of that"`

	mockRefreshDelay time.Duration // test for refresh delay of 0..delay
}

func (options EtcdOptions) OpenURL(url *url.URL) (*EtcdSource, error) {
	switch url.Scheme {
	case "etcd":

	case "etcd+http":
		options.Scheme = "http"
	case "etcd+https":
		options.Scheme = "https"
	}

	for _, host := range strings.Split(url.Host, ",") {
		options.Hosts = append(options.Hosts, host)
	}

	if url.Path != "" {
		options.Prefix = url.Path
	}

	return options.Open()
}

func (options EtcdOptions) String() string {
	return fmt.Sprintf("etcd+%s://%s%s", options.Scheme, strings.Join(options.Hosts, ","), options.Prefix)
}

func (options EtcdOptions) clientConfig() (clientConfig client.Config, err error) {
	for _, host := range options.Hosts {
		endpointURL := url.URL{Scheme: options.Scheme, Host: host}

		clientConfig.Endpoints = append(clientConfig.Endpoints, endpointURL.String())
	}

	return
}

func (options EtcdOptions) Open() (*EtcdSource, error) {
	etcdSource := EtcdSource{
		options: options,
	}

	if clientConfig, err := options.clientConfig(); err != nil {
		return nil, err
	} else if client, err := client.New(clientConfig); err != nil {
		return nil, err
	} else {
		etcdSource.client = client
	}

	etcdSource.keysAPI = client.NewKeysAPI(etcdSource.client)

	return &etcdSource, nil
}

// Undo etcd/client:ClusterError fuckery
// https://github.com/coreos/etcd/pull/4503
func fixupClusterError(err error) error {
	if clusterError, ok := err.(*client.ClusterError); ok {
		var errs []string

		for _, clusterErr := range clusterError.Errors {
			errs = append(errs, clusterErr.Error())
		}

		return fmt.Errorf("%s: %s", clusterError.Error(), strings.Join(errs, "; "))
	} else {
		return err
	}
}

type EtcdSource struct {
	options EtcdOptions

	client  client.Client
	keysAPI client.KeysAPI

	// state to track changes from Scan() to Sync()
	syncIndex uint64

	// refresh nodes
	writeChan chan map[string]Node
	flushChan chan error
}

func (etcd *EtcdSource) String() string {
	return etcd.options.String()
}

func (etcd *EtcdSource) path(parts ...string) string {
	return strings.Join(append([]string{etcd.options.Prefix}, parts...), "/")
}

/*
 * Initialize state in etcd.
 *
 * Creates the top-level config directory if it does not exist, and initialize to follow it
 */
func (etcd *EtcdSource) Init() error {
	if response, err := etcd.keysAPI.Set(context.Background(), etcd.path(), "", &client.SetOptions{Dir: true}); err != nil {
		return fixupClusterError(err)
	} else {
		etcd.syncIndex = response.Node.CreatedIndex
	}

	return nil
}

/*
 * Synchronize current state in etcd.
 *
 * Does a recursive get on the complete /clusterf tree in etcd, and builds the services state from it.
 *
 * Stores the current etcd-index from the snapshot in .syncIndex, so that .Sync() can be used to continue updating any changes.
 */
func (etcd *EtcdSource) Scan() ([]Node, error) {
	response, err := etcd.keysAPI.Get(context.Background(), etcd.path(), &client.GetOptions{Recursive: true})

	if err == nil {

	} else if clientError, ok := err.(client.Error); ok && clientError.Code == client.ErrorCodeKeyNotFound {
		// create directory instead
		return nil, etcd.Init()
	} else {
		return nil, fixupClusterError(err)
	}

	if response.Node.Dir != true {
		return nil, fmt.Errorf("etcd prefix=%s is not a directory", response.Node.Key)
	}

	// the tree root's ModifiedTime may be a long long time in the past, so we can't want to use that for waits
	// we assume this enough to ensure atomic sync with .Watch() on the same tree..
	etcd.syncIndex = response.Index

	// scan, collect and return
	var nodes []Node

	err = etcd.scanNode(response.Node, func(node Node) { nodes = append(nodes, node) })

	return nodes, err
}

func (etcd *EtcdSource) parseNode(etcdNode *client.Node) (node Node, err error) {
	// decode etcd path into config tree path
	path := etcdNode.Key

	if !strings.HasPrefix(path, etcd.options.Prefix) {
		return node, fmt.Errorf("node outside tree: %s", path)
	}

	path = strings.TrimPrefix(path, etcd.options.Prefix)
	path = strings.Trim(path, "/")

	node.Source = etcd
	node.Path = path
	node.IsDir = etcdNode.Dir
	node.Value = etcdNode.Value

	return
}

// Scan through the recursive /clusterf node to return Nodes in pre-order (top-level nodes before their children)
func (etcd *EtcdSource) scanNode(etcdNode *client.Node, handler func(node Node)) error {
	if node, err := etcd.parseNode(etcdNode); err != nil {
		return err
	} else {
		// pre-order
		handler(node)
	}

	// recurse
	for _, childNode := range etcdNode.Nodes {
		if err := etcd.scanNode(childNode, handler); err != nil {
			return err
		}
	}

	return nil
}

/*
 * Watch for changed Nodes in etcd.
 *
 * Sends any changes on the returned channel. Closes the channel on errors.
 */
func (etcd *EtcdSource) Sync(syncChan chan Node) error {
	// kick off new goroutine to handle initial services and updates
	go etcd.watch(syncChan)

	return nil
}

// Watch etcd for changes, and sync them over the chan
func (etcd *EtcdSource) watch(watchChan chan Node) {
	defer close(watchChan)

	watcher := etcd.keysAPI.Watcher(etcd.path(), &client.WatcherOptions{AfterIndex: etcd.syncIndex, Recursive: true})

	for {
		if response, err := watcher.Next(context.Background()); err != nil {
			err = fixupClusterError(err)
			log.Printf("config:EtcdSource.watch: %v", err)
			return
		} else if node, err := etcd.syncNode(response.Action, response.Node); err != nil {
			log.Printf("config:EtcdSource.watch %#v: syncNode: %s", response, err)
			return
		} else {
			log.Printf("config:EtcdSource.watch: %v %v", response.Action, node)
			watchChan <- node
		}
	}
}

// Handle changed node
func (etcd *EtcdSource) syncNode(etcdAction string, etcdNode *client.Node) (Node, error) {
	node, err := etcd.parseNode(etcdNode)
	if err != nil {
		return node, err
	}

	// decode action
	switch etcdAction {
	case "create", "set", "update", "compareAndSwap":

	case "delete", "expire", "compareAndDelete":
		node.Remove = true

	default:
		return node, fmt.Errorf("Unknown etcd action: %s", etcdAction)
	}

	return node, nil
}

// Refresh TTL for existing key that has not yet expired
func (etcd *EtcdSource) refresh(node Node) error {
	var opts = client.SetOptions{
		TTL:     etcd.options.TTL,
		Refresh: true,
	}

	if etcd.options.mockRefreshDelay > 0 {
		time.Sleep(time.Duration(rand.Float64() * float64(etcd.options.mockRefreshDelay)))
	}

	if _, err := etcd.keysAPI.Set(context.Background(), etcd.path(node.Path), "", &opts); err != nil {
		return fixupClusterError(err)
	} else {
		return nil
	}
}

func (etcd *EtcdSource) set(node Node) error {
	var opts = client.SetOptions{
		TTL: etcd.options.TTL,
		Dir: node.IsDir,
	}

	if _, err := etcd.keysAPI.Set(context.Background(), etcd.path(node.Path), node.Value, &opts); err != nil {
		return fixupClusterError(err)
	} else {
		return nil
	}
}

func (etcd *EtcdSource) remove(node Node) error {
	var opts = client.DeleteOptions{
		Dir: node.IsDir,
	}

	if node.IsDir {
		opts.Recursive = true
	}

	if _, err := etcd.keysAPI.Delete(context.Background(), etcd.path(node.Path), &opts); err != nil {
		return fixupClusterError(err)
	} else {
		return nil
	}
}

func (etcd *EtcdSource) writer() {
	defer close(etcd.flushChan)

	var nodes map[string]Node
	var timer = time.Tick(etcd.options.TTL / 2)
	var loopStart, loopEnd time.Time

	for {
		// XXX: how much of our TTL does this write-loop consume...?
		select {
		case refreshTime := <-timer:
			if refreshTime.Before(loopEnd) {
				log.Printf("config:EtcdSource %v: late refresh after %v by %v", etcd, loopEnd.Sub(loopStart), loopEnd.Sub(refreshTime))
			} else {
				log.Printf("config:EtcdSource %v: refresh after %v+%v", etcd, loopEnd.Sub(loopStart), refreshTime.Sub(loopEnd))
			}

			loopStart = time.Now()

			// what happens if we're slow, and our TTLs expire before we can refresh?
			// refresh will probably fail, and we should recover..
			for _, node := range nodes {
				if err := etcd.refresh(node); err == nil {

				} else if err := etcd.set(node); err == nil {
					log.Printf("config:EtcdSource %v: writer: refresh %v: expired", etcd, node)
				} else {
					log.Printf("config:EtcdSource %v: writer: refresh/set %v: %v", etcd, node, err)
				}
			}

			loopEnd = time.Now()

		case writeNodes, open := <-etcd.writeChan:
			loopStart = time.Now()

			// update to new dict
			// if the chan is closed from Flush(), this will get an empty map - and we remove all nodes
			for key, node := range nodes {
				if _, exists := writeNodes[key]; !exists {
					// removed
					if err := etcd.remove(node); err != nil {
						log.Printf("config:EtcdSource %v: writer: remove %v: %v", etcd, node, err)
					} else {
						log.Printf("config:EtcdSource %v: writer: remove %v", etcd, node)
					}
				}
			}
			for key, node := range writeNodes {
				if oldNode, exists := nodes[key]; !exists {
					// new node
					if err := etcd.set(node); err != nil {
						log.Printf("config:EtcdSource %v: writer: new %v: %v", etcd, node, err)
					} else {
						log.Printf("config:EtcdSource %v: writer: new %v", etcd, node)
					}
				} else if !node.Equals(oldNode) {
					// changed
					if err := etcd.set(node); err != nil {
						log.Printf("config:EtcdSource %v: writer: set %v: %v", etcd, node, err)
					} else {
						log.Printf("config:EtcdSource %v: writer: set %v", etcd, node)
					}
				}
			}

			if open {
				// XXX: update in place when actions are succesful instead?
				nodes = writeNodes
			} else {
				// exit
				return
			}

			loopEnd = time.Now()

			log.Printf("config:EtcdSource %v: writer: update in %v", etcd, loopEnd.Sub(loopStart))
		}

	}
}

// Publish a config into etcd. The node will be refreshed per our TTL
func (etcd *EtcdSource) Write(nodes map[string]Node) error {
	if etcd.writeChan == nil {
		etcd.writeChan = make(chan map[string]Node)
		etcd.flushChan = make(chan error)

		go etcd.writer()
	}

	etcd.writeChan <- nodes

	// XXX: errors?
	return nil
}

// Remove all published nodes
func (etcd *EtcdSource) Flush() (err error) {
	if etcd.writeChan != nil {
		close(etcd.writeChan)
	}

	// wait for flush to complete
	for err = range etcd.flushChan {
		log.Printf("config:EtcdSource %v: Flush: %v", etcd, err)
	}

	return
}
<|endoftext|>


<commit_before>package config

import (
	"os"

	"github.com/bcicen/ctop/logging"
)

var (
	Global = NewDefaultConfig()
	log    = logging.Init()
)

type Config struct {
	params  map[string]string
	toggles map[string]bool
	updates chan ConfigMsg
}

// Return param value
func Get(k string) string {
	if _, ok := Global.params[k]; ok == true {
		return Global.params[k]
	}
	return ""
}

// Return toggle value
func GetToggle(k string) bool {
	if _, ok := Global.toggles[k]; ok == true {
		return Global.toggles[k]
	}
	return false
}

// Toggle a boolean option
func Toggle(k string) {
	Global.toggles[k] = Global.toggles[k] != true
}

type ConfigMsg struct {
	key string
	val string
}

func Update(k, v string) {
	log.Noticef("config update: %s = %s", k, v)
	Global.updates <- ConfigMsg{k, v}
}

func NewDefaultConfig() Config {
	docker := os.Getenv("DOCKER_HOST")
	if docker == "" {
		docker = "unix:///var/run/docker.sock"
	}

	params := map[string]string{
		"dockerHost": docker,
		"filterStr":  "",
		"sortField":  "id",
	}

	toggles := map[string]bool{
		"sortReverse":    false,
		"enableHeader":   false,
		"loggingEnabled": true,
	}

	config := Config{params, toggles, make(chan ConfigMsg)}
	go func() {
		for m := range config.updates {
			config.params[m.key] = m.val
		}
	}()
	return config
}
<commit_msg>add log message to config toggle<commit_after>package config

import (
	"os"

	"github.com/bcicen/ctop/logging"
)

var (
	Global = NewDefaultConfig()
	log    = logging.Init()
)

type Config struct {
	params  map[string]string
	toggles map[string]bool
	updates chan ConfigMsg
}

// Return param value
func Get(k string) string {
	if _, ok := Global.params[k]; ok == true {
		return Global.params[k]
	}
	return ""
}

// Return toggle value
func GetToggle(k string) bool {
	if _, ok := Global.toggles[k]; ok == true {
		return Global.toggles[k]
	}
	return false
}

// Toggle a boolean option
func Toggle(k string) {
	Global.toggles[k] = Global.toggles[k] != true
	log.Noticef("config change: %s = %t", k, Global.toggles[k])
}

type ConfigMsg struct {
	key string
	val string
}

func Update(k, v string) {
	Global.updates <- ConfigMsg{k, v}
}

func NewDefaultConfig() Config {
	docker := os.Getenv("DOCKER_HOST")
	if docker == "" {
		docker = "unix:///var/run/docker.sock"
	}

	params := map[string]string{
		"dockerHost": docker,
		"filterStr":  "",
		"sortField":  "id",
	}

	toggles := map[string]bool{
		"sortReverse":    false,
		"enableHeader":   false,
		"loggingEnabled": true,
	}

	config := Config{params, toggles, make(chan ConfigMsg)}
	go func() {
		for m := range config.updates {
			config.params[m.key] = m.val
			log.Noticef("config change: %s = %s", m.key, m.val)
		}
	}()
	return config
}
<|endoftext|>


<commit_before>package fire

import (
	"errors"

	"gopkg.in/mgo.v2/bson"
)

// A Callback allows further extensibility and customisation of the API.
type Callback func(*Context) (error, error)

// Combine combines multiple callbacks to one.
func Combine(callbacks ...Callback) Callback {
	return func(ctx *Context) (error, error) {
		// call all callbacks
		for _, cb := range callbacks {
			err, sysErr := cb(ctx)

			// return early if an error occurs
			if err != nil || sysErr != nil {
				return err, sysErr
			}
		}

		return nil, nil
	}
}

// A DependentResourcesValidator counts documents in the supplied collections
// and returns an error if some get found. This callback is meant to protect
// resources from breaking relations when requested to be deleted.
func DependentResourcesValidator(relations map[string]string) Callback {
	return func(ctx *Context) (error, error) {
		// only run validator on Delete
		if ctx.Action != Delete {
			return nil, nil
		}

		// check all relations
		for coll, field := range relations {
			// count referencing documents
			n, err := ctx.DB.C(coll).Find(bson.M{field: ctx.ID}).Limit(1).Count()
			if err != nil {
				return nil, err
			}

			// immediately return if a document is found
			if n == 1 {
				return errors.New("resource has dependent resources"), nil
			}
		}

		// pass validation
		return nil, nil
	}
}

// A VerifyReferencesValidator makes sure all references in the document are
// existing by counting on the related collections.
func VerifyReferencesValidator(relations map[string]string) Callback {
	return func(ctx *Context) (error, error) {
		// only run validator on Create and Update
		if ctx.Action != Create && ctx.Action != Update {
			return nil, nil
		}

		// check all relations
		for relation, collection := range relations {
			// read referenced fire.Resource id
			id := ctx.Model.ReferenceID(relation)
			if id == nil {
				continue
			}

			// count entities in database
			n, err := ctx.DB.C(collection).FindId(id).Limit(1).Count()
			if err != nil {
				return nil, err
			}

			// check for existence
			if n != 1 {
				return errors.New("missing required relationship " + relation), nil
			}
		}

		// pass validation
		return nil, nil
	}
}
<commit_msg>fix doc<commit_after>package fire

import (
	"errors"

	"gopkg.in/mgo.v2/bson"
)

// A Callback allows further extensibility and customisation of the API.
type Callback func(*Context) (error, error)

// Combine combines multiple callbacks to one.
func Combine(callbacks ...Callback) Callback {
	return func(ctx *Context) (error, error) {
		// call all callbacks
		for _, cb := range callbacks {
			err, sysErr := cb(ctx)

			// return early if an error occurs
			if err != nil || sysErr != nil {
				return err, sysErr
			}
		}

		return nil, nil
	}
}

// DependentResourcesValidator counts documents in the supplied collections
// and returns an error if some get found. This callback is meant to protect
// resources from breaking relations when requested to be deleted.
func DependentResourcesValidator(relations map[string]string) Callback {
	return func(ctx *Context) (error, error) {
		// only run validator on Delete
		if ctx.Action != Delete {
			return nil, nil
		}

		// check all relations
		for coll, field := range relations {
			// count referencing documents
			n, err := ctx.DB.C(coll).Find(bson.M{field: ctx.ID}).Limit(1).Count()
			if err != nil {
				return nil, err
			}

			// immediately return if a document is found
			if n == 1 {
				return errors.New("resource has dependent resources"), nil
			}
		}

		// pass validation
		return nil, nil
	}
}

// VerifyReferencesValidator makes sure all references in the document are
// existing by counting on the related collections.
func VerifyReferencesValidator(relations map[string]string) Callback {
	return func(ctx *Context) (error, error) {
		// only run validator on Create and Update
		if ctx.Action != Create && ctx.Action != Update {
			return nil, nil
		}

		// check all relations
		for relation, collection := range relations {
			// read referenced fire.Resource id
			id := ctx.Model.ReferenceID(relation)
			if id == nil {
				continue
			}

			// count entities in database
			n, err := ctx.DB.C(collection).FindId(id).Limit(1).Count()
			if err != nil {
				return nil, err
			}

			// check for existence
			if n != 1 {
				return errors.New("missing required relationship " + relation), nil
			}
		}

		// pass validation
		return nil, nil
	}
}
<|endoftext|>


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

package encrypt

import (
	"bytes"
	"testing"
)

func TestRoundTrip(t *testing.T) {
	src := bytes.NewReader([]byte("hello world"))
	dst := &bytes.Buffer{}
	roundtrip := &bytes.Buffer{}

	ec, err := NewEtmCryptor([]byte("hellohelloworld1hellohelloworld1"))
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	err = ec.Encrypt(src, dst)

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

	enreader := bytes.NewReader(dst.Bytes())
	err = ec.Decrypt(enreader, roundtrip)
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	st := roundtrip.String()
	if st != "hello world" {
		t.Fatal("Failed round trip.")
	}
}

func TestTampered(t *testing.T) {
	src := bytes.NewReader([]byte("hello world"))
	dst := &bytes.Buffer{}
	roundtrip := &bytes.Buffer{}

	ec, err := NewEtmCryptor([]byte("hellohelloworld1hellohelloworld1"))
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	err = ec.Encrypt(src, dst)

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

	b := dst.Bytes()
	b[43] += 1

	enreader := bytes.NewReader(b)
	err = ec.Decrypt(enreader, roundtrip)
	if err != nil {
		return
	}
	t.Fatalf("Missing error from tampered data: enreader:%v", enreader)

}

func TestEtmName(t *testing.T) {
	ec, err := NewEtmCryptor([]byte("hellohelloworld1hellohelloworld1"))
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	fname := "hello-world-1.2.3.4.tar.gz"

	s, err := ec.EncryptName(fname)
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	fname2, err := ec.DecryptName(s)
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	if fname != fname2 {
		t.Fatalf("error: expected decrypted name of %s, but got %s", fname, fname2)
	}

	tampered := s + "b"

	fname2, err = ec.DecryptName(tampered)
	if err == nil {
		t.Fatalf("expected error due to tampering, didn't get one.")
	}
}
<commit_msg>improve tampering test.<commit_after>/**
 *  Copyright 2014 Paul Querna
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package encrypt

import (
	"bytes"
	"strings"
	"testing"
)

func TestRoundTrip(t *testing.T) {
	src := bytes.NewReader([]byte("hello world"))
	dst := &bytes.Buffer{}
	roundtrip := &bytes.Buffer{}

	ec, err := NewEtmCryptor([]byte("hellohelloworld1hellohelloworld1"))
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	err = ec.Encrypt(src, dst)

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

	enreader := bytes.NewReader(dst.Bytes())
	err = ec.Decrypt(enreader, roundtrip)
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	st := roundtrip.String()
	if st != "hello world" {
		t.Fatal("Failed round trip.")
	}
}

func TestTampered(t *testing.T) {
	src := bytes.NewReader([]byte("hello world"))
	dst := &bytes.Buffer{}
	roundtrip := &bytes.Buffer{}

	ec, err := NewEtmCryptor([]byte("hellohelloworld1hellohelloworld1"))
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	err = ec.Encrypt(src, dst)

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

	b := dst.Bytes()
	b[43] += 1

	enreader := bytes.NewReader(b)
	err = ec.Decrypt(enreader, roundtrip)
	if err != nil {
		return
	}
	t.Fatalf("Missing error from tampered data: enreader:%v", enreader)

}

func TestEtmName(t *testing.T) {
	ec, err := NewEtmCryptor([]byte("hellohelloworld1hellohelloworld1"))
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	fname := "hello-world-1.2.3.4.tar.gz"

	s, err := ec.EncryptName(fname)
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	fname2, err := ec.DecryptName(s)
	if err != nil {
		t.Fatalf("error: %v", err)
	}

	if fname != fname2 {
		t.Fatalf("error: expected decrypted name of %s, but got %s", fname, fname2)
	}

	tampered := strings.ToUpper(s)

	fname2, err = ec.DecryptName(tampered)
	if err == nil {
		t.Fatalf("expected error due to tampering, didn't get one.")
	}
}
<|endoftext|>


<commit_before>package selectgo

import "testing"

func TestAssemble(t *testing.T) {

	should := []struct {
		pass *QueryStatement
		want string
	}{
		{
			NewQueryStatement().Select([]string{"uuid()"}),
			"SELECT uuid()",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table"),
			"SELECT * FROM table",
		},
		{
			NewQueryStatement().Select([]string{"id"}).From("table").Where("1 = 1"),
			"SELECT id FROM table WHERE 1 = 1",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table"),
			"SELECT * FROM table",
		},

		{
			NewQueryStatement().Select([]string{"*"}).From("table").Limit(1, 50),
			"SELECT * FROM table LIMIT 50 OFFSET 1",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").Rowcount(50).Offset(1),
			"SELECT * FROM table LIMIT 50 OFFSET 1",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").Rowcount(50).Offset(0),
			"SELECT * FROM table LIMIT 50",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").Rowcount(0).Offset(0),
			"SELECT * FROM table",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").OrderBy("created ASC"),
			"SELECT * FROM table ORDER BY created ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table ORDER BY created DESC, name ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table AS t").InnerJoin("anothertable AS at ON at.id = t.id").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table AS t INNER JOIN anothertable AS at ON at.id = t.id ORDER BY created DESC, name ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table AS t").InnerJoin("anothertable AS at ON at.id = t.id").LeftJoin("thirdtable AS tt ON tt.id = t.id").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table AS t INNER JOIN anothertable AS at ON at.id = t.id LEFT JOIN thirdtable AS tt ON tt.id = t.id ORDER BY created DESC, name ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table AS t").LeftJoin("fourthtable AS ft ON ft.id = t.id").InnerJoin("anothertable AS at ON at.id = t.id").LeftJoin("thirdtable AS tt ON tt.id = t.id").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table AS t LEFT JOIN fourthtable AS ft ON ft.id = t.id INNER JOIN anothertable AS at ON at.id = t.id LEFT JOIN thirdtable AS tt ON tt.id = t.id ORDER BY created DESC, name ASC",
		},
	}

	for _, s := range should {
		r, err := s.pass.Assemble()
		if r != s.want {
			t.Errorf("Failed\n expected %q\n got %q", s.want, r)
		}
		if err != nil {
			t.Errorf("Error should be nil\n got %v", err)
		}
	}
}

func TestFailures(t *testing.T) {
	must := []struct {
		fail *QueryStatement
		want string
	}{
		{
			NewQueryStatement().Select([]string{}),
			"",
		},
		{
			NewQueryStatement().Select([]string{""}),
			"",
		},
		{
			NewQueryStatement().Select([]string{" "}).From("table"),
			"",
		},
	}

	for _, s := range must {
		r, err := s.fail.Assemble()
		if err == nil {
			t.Errorf("Error shouldn't be nil, failed on %q", r)
		}
	}
}
<commit_msg>Added another test<commit_after>package selectgo

import "testing"

func TestAssemble(t *testing.T) {

	should := []struct {
		pass *QueryStatement
		want string
	}{
		{
			NewQueryStatement().Select([]string{"uuid()"}),
			"SELECT uuid()",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table"),
			"SELECT * FROM table",
		},
		{
			NewQueryStatement().Select([]string{"id"}).From("table").Where("1 = 1"),
			"SELECT id FROM table WHERE 1 = 1",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table"),
			"SELECT * FROM table",
		},

		{
			NewQueryStatement().Select([]string{"*"}).From("table").Limit(1, 50),
			"SELECT * FROM table LIMIT 50 OFFSET 1",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").Rowcount(50).Offset(1),
			"SELECT * FROM table LIMIT 50 OFFSET 1",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").Rowcount(50).Offset(0),
			"SELECT * FROM table LIMIT 50",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").Rowcount(0).Offset(0),
			"SELECT * FROM table",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").OrderBy("created ASC"),
			"SELECT * FROM table ORDER BY created ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table ORDER BY created DESC, name ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table AS t").InnerJoin("anothertable AS at ON at.id = t.id").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table AS t INNER JOIN anothertable AS at ON at.id = t.id ORDER BY created DESC, name ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table AS t").InnerJoin("anothertable AS at ON at.id = t.id").InnerJoin("yetanothertable AS yat ON yat.id = at.id").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table AS t INNER JOIN anothertable AS at ON at.id = t.id INNER JOIN yetanothertable AS yat ON yat.id = at.id ORDER BY created DESC, name ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table AS t").InnerJoin("anothertable AS at ON at.id = t.id").LeftJoin("thirdtable AS tt ON tt.id = t.id").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table AS t INNER JOIN anothertable AS at ON at.id = t.id LEFT JOIN thirdtable AS tt ON tt.id = t.id ORDER BY created DESC, name ASC",
		},
		{
			NewQueryStatement().Select([]string{"*"}).From("table AS t").LeftJoin("fourthtable AS ft ON ft.id = t.id").InnerJoin("anothertable AS at ON at.id = t.id").LeftJoin("thirdtable AS tt ON tt.id = t.id").OrderBy("created DESC, name ASC"),
			"SELECT * FROM table AS t LEFT JOIN fourthtable AS ft ON ft.id = t.id INNER JOIN anothertable AS at ON at.id = t.id LEFT JOIN thirdtable AS tt ON tt.id = t.id ORDER BY created DESC, name ASC",
		},
	}

	for _, s := range should {
		r, err := s.pass.Assemble()
		if r != s.want {
			t.Errorf("Failed\n expected %q\n got %q", s.want, r)
		}
		if err != nil {
			t.Errorf("Error should be nil\n got %v", err)
		}
	}
}

func TestFailures(t *testing.T) {
	must := []struct {
		fail *QueryStatement
		want string
	}{
		{
			NewQueryStatement().Select([]string{}),
			"",
		},
		{
			NewQueryStatement().Select([]string{""}),
			"",
		},
		{
			NewQueryStatement().Select([]string{" "}).From("table"),
			"",
		},
	}

	for _, s := range must {
		r, err := s.fail.Assemble()
		if err == nil {
			t.Errorf("Error shouldn't be nil, failed on %q", r)
		}
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"log"
	"net/http"
	"os"
)

func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		apiKey := r.Header.Get("FORWARDLYTICS_API_KEY")
		if apiKey != os.Getenv("FORWARDLYTICS_API_KEY") {
			log.Printf("Wrong API key. We had '%s' but it should be '%s'\n", apiKey, os.Getenv("FORWARDLYTICS_API_KEY"))

			errorMsg := "Invalid API KEY. The FORWARDLYTICS_API_KEY header must be specified, with the proper API key."
			writeResponse(w, errorMsg, http.StatusUnauthorized)
			return
		}
		next.ServeHTTP(w, r)
	})
}
<commit_msg>remove leaking of API key<commit_after>package handlers

import (
	"net/http"
	"os"
)

func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		apiKey := r.Header.Get("FORWARDLYTICS_API_KEY")
		if apiKey != os.Getenv("FORWARDLYTICS_API_KEY") {
			errorMsg := "Invalid API KEY. The FORWARDLYTICS_API_KEY header must be specified, with the proper API key."
			writeResponse(w, errorMsg, http.StatusUnauthorized)
			return
		}
		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"image"
	_ "image/png"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/generaltso/vibrant"
	"gopkg.in/gographics/imagick.v3/imagick"
)

func getImageColors(file string) (fgColor, bgColor string) {
	checkErr := func(err error) {
		if err != nil {
			panic(err)
		}
	}

	f, err := os.Open(file)
	checkErr(err)
	defer f.Close()

	img, _, err := image.Decode(f)
	checkErr(err)

	palette, err := vibrant.NewPaletteFromImage(img)
	checkErr(err)

	for name, swatch := range palette.ExtractAwesome() {
		if name == "Vibrant" {
			fgColor = swatch.Color.TitleTextColor().RGBHex()
			bgColor = swatch.Color.RGBHex()
		}
	}
	if fgColor == bgColor {
		fgColor = "#000"
	}
	return fgColor, bgColor
}

func createStoryText(fgColor, bgColor, text string) *imagick.MagickWand {
	mw := imagick.NewMagickWand()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	pw.SetColor("none")
	mw.NewImage(1080, 103, pw)

	dw.SetFont("TTCommonsB")
	dw.SetFontSize(80)

	textMetrics := mw.QueryFontMetrics(dw, text)

	textWidth := textMetrics.TextWidth

	pw.SetColor(bgColor)
	dw.SetFillColor(pw)

	dw.RoundRectangle((1080-textWidth)/2-20, 0, (1080-textWidth)/2+textWidth+20, 103, 10, 10)

	pw.SetColor(fgColor)
	dw.SetFillColor(pw)
	dw.SetTextAlignment(imagick.ALIGN_CENTER)
	dw.Annotation(1080/2, 93-17, text)

	mw.DrawImage(dw)

	return mw
}

func downloadTeamLogo(filepath, url string) (err error) {
	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Check server response
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status: %s", resp.Status)
	}

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func getTeamsLogo(radiantTeamID, direTeamID int) {
	teamsData := getTeamsData()
	for _, team := range teamsData {
		teamID := team.TeamID
		if teamID == radiantTeamID || teamID == direTeamID {
			err := downloadTeamLogo(fmt.Sprintf("tmp/teams/%d.png", teamID), team.LogoURL)
			if err != nil {
				log.Printf("Error downloading %d team logo", teamID)
			}
		}
	}
}

func guessResizeSize(width, height uint) (wOut, hOut uint) {
	if width > height {
		mult := 294 / width
		wOut = width * mult
		hOut = height * mult
	} else {
		mult := 294 / height
		wOut = width * mult
		hOut = height * mult
	}
	return wOut, hOut
}

func makeStoryImage(matchData oDotaMatchData) {
	radiantTeamName := matchData.RadiantName
	direTeamName := matchData.DireName
	radiantTeamID := matchData.RadiantTeamID
	direTeamID := matchData.DireTeamID
	matchID := matchData.MatchID

	getTeamsLogo(radiantTeamID, direTeamID)

	direTeamFile := fmt.Sprintf("tmp/teams/%d.png", direTeamID)
	radiantTeamFile := fmt.Sprintf("tmp/teams/%d.png", radiantTeamID)

	mw := imagick.NewMagickWand()
	defer mw.Destroy()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	dw.SetFont("TTCommonsB")

	pw.SetColor("#242424")
	mw.NewImage(1080, 1920, pw)

	fgColor, bgColor := getImageColors(direTeamFile)

	direTeamText := createStoryText(fgColor, bgColor, direTeamName)

	mw.CompositeImage(direTeamText, imagick.COMPOSITE_OP_OVER, true, 0, ((1920/2)-294)/2+250)

	direTeamText.Destroy()

	fgColor, bgColor = getImageColors(radiantTeamFile)

	radiantTeamText := createStoryText(fgColor, bgColor, radiantTeamName)

	mw.CompositeImage(radiantTeamText, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2+((1920/2)-294)/2+250)

	radiantTeamText.Destroy()

	direTeamLogo := imagick.NewMagickWand()
	direTeamLogo.ReadImage(direTeamFile)

	width := direTeamLogo.GetImageWidth()
	height := direTeamLogo.GetImageHeight()
	wGuessed, hGuessed := guessResizeSize(width, height)

	direTeamLogo.ResizeImage(wGuessed, hGuessed, imagick.FILTER_CUBIC)
	mw.CompositeImage(direTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, ((1920/2)-294)/2-100)
	direTeamLogo.Destroy()

	radiantTeamLogo := imagick.NewMagickWand()
	radiantTeamLogo.ReadImage(radiantTeamFile)

	width = radiantTeamLogo.GetImageWidth()
	height = radiantTeamLogo.GetImageHeight()
	wGuessed, hGuessed = guessResizeSize(width, height)

	radiantTeamLogo.ResizeImage(wGuessed, hGuessed, imagick.FILTER_CUBIC)
	mw.CompositeImage(radiantTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, 1920/2+((1920/2)-294)/2-100)
	radiantTeamLogo.Destroy()

	vs := createStoryText("#242424", "#ffffff", "VS")
	mw.CompositeImage(vs, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2-51)
	vs.Destroy()

	mw.DrawImage(dw)
	storyFile := fmt.Sprintf("tmp/s%d.png", matchID)
	mw.WriteImage(storyFile)
}
<commit_msg>Fix empty logo<commit_after>package main

import (
	"fmt"
	"image"
	_ "image/png"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/generaltso/vibrant"
	"gopkg.in/gographics/imagick.v3/imagick"
)

func getImageColors(file string) (fgColor, bgColor string) {
	checkErr := func(err error) {
		if err != nil {
			panic(err)
		}
	}

	f, err := os.Open(file)
	checkErr(err)
	defer f.Close()

	img, _, err := image.Decode(f)
	checkErr(err)

	palette, err := vibrant.NewPaletteFromImage(img)
	checkErr(err)

	for name, swatch := range palette.ExtractAwesome() {
		if name == "Vibrant" {
			fgColor = swatch.Color.TitleTextColor().RGBHex()
			bgColor = swatch.Color.RGBHex()
		}
	}
	if fgColor == bgColor {
		fgColor = "#000"
	}
	return fgColor, bgColor
}

func createStoryText(fgColor, bgColor, text string) *imagick.MagickWand {
	mw := imagick.NewMagickWand()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	pw.SetColor("none")
	mw.NewImage(1080, 103, pw)

	dw.SetFont("TTCommonsB")
	dw.SetFontSize(80)

	textMetrics := mw.QueryFontMetrics(dw, text)

	textWidth := textMetrics.TextWidth

	pw.SetColor(bgColor)
	dw.SetFillColor(pw)

	dw.RoundRectangle((1080-textWidth)/2-20, 0, (1080-textWidth)/2+textWidth+20, 103, 10, 10)

	pw.SetColor(fgColor)
	dw.SetFillColor(pw)
	dw.SetTextAlignment(imagick.ALIGN_CENTER)
	dw.Annotation(1080/2, 93-17, text)

	mw.DrawImage(dw)

	return mw
}

func downloadTeamLogo(filepath, url string) (err error) {
	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Check server response
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status: %s", resp.Status)
	}

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func getTeamsLogo(radiantTeamID, direTeamID int) {
	teamsData := getTeamsData()
	for _, team := range teamsData {
		teamID := team.TeamID
		if teamID == radiantTeamID || teamID == direTeamID {
			logo := team.LogoURL
			if logo == "" {
				logo = "https://github.com/dotaspirit/dotaspirit/raw/master/assets/images/nologo.png"
			}
			err := downloadTeamLogo(fmt.Sprintf("tmp/teams/%d.png", teamID), logo)
			if err != nil {
				log.Printf("Error downloading %d team logo", teamID)
			}
		}
	}
}

func guessResizeSize(width, height uint) (wOut, hOut uint) {
	if width > height {
		mult := 294 / width
		wOut = width * mult
		hOut = height * mult
	} else {
		mult := 294 / height
		wOut = width * mult
		hOut = height * mult
	}
	return wOut, hOut
}

func makeStoryImage(matchData oDotaMatchData) {
	radiantTeamName := matchData.RadiantName
	direTeamName := matchData.DireName
	radiantTeamID := matchData.RadiantTeamID
	direTeamID := matchData.DireTeamID
	matchID := matchData.MatchID

	getTeamsLogo(radiantTeamID, direTeamID)

	direTeamFile := fmt.Sprintf("tmp/teams/%d.png", direTeamID)
	radiantTeamFile := fmt.Sprintf("tmp/teams/%d.png", radiantTeamID)

	mw := imagick.NewMagickWand()
	defer mw.Destroy()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	dw.SetFont("TTCommonsB")

	pw.SetColor("#242424")
	mw.NewImage(1080, 1920, pw)

	fgColor, bgColor := getImageColors(direTeamFile)

	direTeamText := createStoryText(fgColor, bgColor, direTeamName)

	mw.CompositeImage(direTeamText, imagick.COMPOSITE_OP_OVER, true, 0, ((1920/2)-294)/2+250)

	direTeamText.Destroy()

	fgColor, bgColor = getImageColors(radiantTeamFile)

	radiantTeamText := createStoryText(fgColor, bgColor, radiantTeamName)

	mw.CompositeImage(radiantTeamText, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2+((1920/2)-294)/2+250)

	radiantTeamText.Destroy()

	direTeamLogo := imagick.NewMagickWand()
	direTeamLogo.ReadImage(direTeamFile)

	width := direTeamLogo.GetImageWidth()
	height := direTeamLogo.GetImageHeight()
	wGuessed, hGuessed := guessResizeSize(width, height)

	direTeamLogo.ResizeImage(wGuessed, hGuessed, imagick.FILTER_CUBIC)
	mw.CompositeImage(direTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, ((1920/2)-294)/2-100)
	direTeamLogo.Destroy()

	radiantTeamLogo := imagick.NewMagickWand()
	radiantTeamLogo.ReadImage(radiantTeamFile)

	width = radiantTeamLogo.GetImageWidth()
	height = radiantTeamLogo.GetImageHeight()
	wGuessed, hGuessed = guessResizeSize(width, height)

	radiantTeamLogo.ResizeImage(wGuessed, hGuessed, imagick.FILTER_CUBIC)
	mw.CompositeImage(radiantTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, 1920/2+((1920/2)-294)/2-100)
	radiantTeamLogo.Destroy()

	vs := createStoryText("#242424", "#ffffff", "VS")
	mw.CompositeImage(vs, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2-51)
	vs.Destroy()

	mw.DrawImage(dw)
	storyFile := fmt.Sprintf("tmp/s%d.png", matchID)
	mw.WriteImage(storyFile)
}
<|endoftext|>


<commit_before>package handlers

import (
	"github.com/bbiskup/edify-web/defs"
	csp "github.com/bbiskup/edify/edifact/spec/codes"
	dsp "github.com/bbiskup/edify/edifact/spec/dataelement"
	msp "github.com/bbiskup/edify/edifact/spec/message"
	ssp "github.com/bbiskup/edify/edifact/spec/segment"
	"html/template"
	"log"
	"net/http"
	"sort"
	"strings"
)

var specSearchTemplates *template.Template

func init() {
	funcMap := template.FuncMap{
		"MsgSpecURL":      MsgSpecURL,
		"SegSpecURL":      SegSpecURL,
		"DataElemSpecURL": DataElemSpecURL,
		"CodesSpecURL":    CodesSpecURL,
	}
	t := template.New("layout.html").Funcs(funcMap)
	specSearchTemplates = template.Must(t.ParseFiles(
		defs.TemplatePaths("layout.html", "navbar.html", "specsearch.html")...,
	))
}

// Search term in message specifications
func searchMsgSpecs(w http.ResponseWriter, searchTerm string) []*msp.MsgSpec {
	result := msp.MsgSpecs{}
	for msgId, msgSpec := range defs.Validator.MsgSpecs {
		if strings.Contains(strings.ToLower(msgId), searchTerm) || strings.Contains(strings.ToLower(msgSpec.Name), searchTerm) {
			result = append(result, msgSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in segment specifications
func searchSegSpecs(w http.ResponseWriter, searchTerm string) []*ssp.SegSpec {
	result := ssp.SegSpecs{}
	for _, segID := range defs.Validator.SegSpecs.Ids() {
		segSpec := defs.Validator.SegSpecs.Get(segID)
		if strings.Contains(strings.ToLower(segID), searchTerm) || strings.Contains(strings.ToLower(segSpec.Name), searchTerm) {
			result = append(result, segSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in composite data element specifications
func searchCompositeDataElemSpecs(w http.ResponseWriter, searchTerm string) []*dsp.CompositeDataElemSpec {
	result := dsp.CompositeDataElemSpecs{}
	for _, dataElemSpec := range defs.SpecParser.CompositeDataElemSpecs {
		id := dataElemSpec.Id()
		if strings.Contains(strings.ToLower(id), searchTerm) || strings.Contains(strings.ToLower(dataElemSpec.Name()), searchTerm) {
			result = append(result, dataElemSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in simple data element specifications
func searchSimpleDataElemSpecs(w http.ResponseWriter, searchTerm string) []*dsp.SimpleDataElemSpec {
	result := dsp.SimpleDataElemSpecs{}
	for _, dataElemSpec := range defs.SpecParser.SimpleDataElemSpecs {
		id := dataElemSpec.Id()
		if strings.Contains(strings.ToLower(id), searchTerm) || strings.Contains(strings.ToLower(dataElemSpec.Name()), searchTerm) {
			result = append(result, dataElemSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in code specs
func searchCodesSpecs(w http.ResponseWriter, searchTerm string) []*csp.CodesSpec {
	result := []*csp.CodesSpec{}
	for _, codesSpec := range defs.SpecParser.CodesSpecs {
		id := codesSpec.Id
		if strings.Contains(strings.ToLower(id), searchTerm) || strings.Contains(strings.ToLower(codesSpec.Name), searchTerm) {
			result = append(result, codesSpec)
		}
	}
	return result
}

func SpecSearch(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	searchTerm := strings.ToLower(r.FormValue("searchterm"))
	data := map[string]interface{}{}
	//log.Printf("Found %d message specs for search term %s", len(msgSpecs), searchTerm)
	data["msgSpecs"] = searchMsgSpecs(w, searchTerm)
	data["segSpecs"] = searchSegSpecs(w, searchTerm)
	data["compositeDataElemSpecs"] = searchCompositeDataElemSpecs(w, searchTerm)
	data["simpleDataElemSpecs"] = searchSimpleDataElemSpecs(w, searchTerm)
	data["codesSpecs"] = searchCodesSpecs(w, searchTerm)
	data["searchTerm"] = searchTerm

	err := specSearchTemplates.ExecuteTemplate(w, "layout", data)
	if err != nil {
		log.Printf("Error executing template: %s", err)
	}
}
<commit_msg>Simplified signatures of spec search methods<commit_after>package handlers

import (
	"github.com/bbiskup/edify-web/defs"
	csp "github.com/bbiskup/edify/edifact/spec/codes"
	dsp "github.com/bbiskup/edify/edifact/spec/dataelement"
	msp "github.com/bbiskup/edify/edifact/spec/message"
	ssp "github.com/bbiskup/edify/edifact/spec/segment"
	"html/template"
	"log"
	"net/http"
	"sort"
	"strings"
)

var specSearchTemplates *template.Template

func init() {
	funcMap := template.FuncMap{
		"MsgSpecURL":      MsgSpecURL,
		"SegSpecURL":      SegSpecURL,
		"DataElemSpecURL": DataElemSpecURL,
		"CodesSpecURL":    CodesSpecURL,
	}
	t := template.New("layout.html").Funcs(funcMap)
	specSearchTemplates = template.Must(t.ParseFiles(
		defs.TemplatePaths("layout.html", "navbar.html", "specsearch.html")...,
	))
}

// Search term in message specifications
func searchMsgSpecs(searchTerm string) []*msp.MsgSpec {
	result := msp.MsgSpecs{}
	for msgId, msgSpec := range defs.Validator.MsgSpecs {
		if strings.Contains(strings.ToLower(msgId), searchTerm) || strings.Contains(strings.ToLower(msgSpec.Name), searchTerm) {
			result = append(result, msgSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in segment specifications
func searchSegSpecs(searchTerm string) []*ssp.SegSpec {
	result := ssp.SegSpecs{}
	for _, segID := range defs.Validator.SegSpecs.Ids() {
		segSpec := defs.Validator.SegSpecs.Get(segID)
		if strings.Contains(strings.ToLower(segID), searchTerm) || strings.Contains(strings.ToLower(segSpec.Name), searchTerm) {
			result = append(result, segSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in composite data element specifications
func searchCompositeDataElemSpecs(searchTerm string) []*dsp.CompositeDataElemSpec {
	result := dsp.CompositeDataElemSpecs{}
	for _, dataElemSpec := range defs.SpecParser.CompositeDataElemSpecs {
		id := dataElemSpec.Id()
		if strings.Contains(strings.ToLower(id), searchTerm) || strings.Contains(strings.ToLower(dataElemSpec.Name()), searchTerm) {
			result = append(result, dataElemSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in simple data element specifications
func searchSimpleDataElemSpecs(searchTerm string) []*dsp.SimpleDataElemSpec {
	result := dsp.SimpleDataElemSpecs{}
	for _, dataElemSpec := range defs.SpecParser.SimpleDataElemSpecs {
		id := dataElemSpec.Id()
		if strings.Contains(strings.ToLower(id), searchTerm) || strings.Contains(strings.ToLower(dataElemSpec.Name()), searchTerm) {
			result = append(result, dataElemSpec)
		}
	}
	sort.Sort(result)
	return result
}

// Search term in code specs
func searchCodesSpecs(searchTerm string) []*csp.CodesSpec {
	result := []*csp.CodesSpec{}
	for _, codesSpec := range defs.SpecParser.CodesSpecs {
		id := codesSpec.Id
		if strings.Contains(strings.ToLower(id), searchTerm) || strings.Contains(strings.ToLower(codesSpec.Name), searchTerm) {
			result = append(result, codesSpec)
		}
	}
	return result
}

func SpecSearch(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	searchTerm := strings.ToLower(r.FormValue("searchterm"))
	data := map[string]interface{}{}
	//log.Printf("Found %d message specs for search term %s", len(msgSpecs), searchTerm)
	data["msgSpecs"] = searchMsgSpecs(searchTerm)
	data["segSpecs"] = searchSegSpecs(searchTerm)
	data["compositeDataElemSpecs"] = searchCompositeDataElemSpecs(searchTerm)
	data["simpleDataElemSpecs"] = searchSimpleDataElemSpecs(searchTerm)
	data["codesSpecs"] = searchCodesSpecs(searchTerm)
	data["searchTerm"] = searchTerm

	err := specSearchTemplates.ExecuteTemplate(w, "layout", data)
	if err != nil {
		log.Printf("Error executing template: %s", err)
	}
}
<|endoftext|>


<commit_before>package dbf

import (
	"testing"
	"time"
)

func TestToFloat64(t *testing.T) {
	if ToFloat64(123.456) != float64(123.456) {
		t.Errorf("Want %f, have %f", float64(123.456), ToFloat64(123.456))
	}
	if ToFloat64("123.456") != float64(0) {
		t.Errorf("Want %f, have %f", 0, ToFloat64(123.456))
	}
}

func TestToInt64(t *testing.T) {
	if ToInt64(int64(123456)) != int64(123456) {
		t.Errorf("Want %d, have %d", int64(123456), ToInt64(123456))
	}
	if ToInt64("123.456") != int64(0) {
		t.Errorf("Want %d, have %d", 0, ToInt64(123456))
	}
}

func TestToString(t *testing.T) {
	if ToString("Hêllo!") != "Hêllo!" {
		t.Errorf("Want %q, have %q", "Hêllo!", ToString("Hêllo!"))
	}
	if ToString(123.456) != "" {
		t.Errorf("Want %q, have %q", "", ToString(123.456))
	}
}

func TestToTrimmedString(t *testing.T) {
	if ToTrimmedString("Hêllo!      ") != "Hêllo!" {
		t.Errorf("Want %q, have %q", "Hêllo!", ToTrimmedString("Hêllo!    "))
	}
	if ToTrimmedString(123.456) != "" {
		t.Errorf("Want %q, have %q", "", ToTrimmedString(123.456))
	}
}

func TestToTime(t *testing.T) {
	now := time.Now()
	if ToTime(now).Equal(now) == false {
		t.Errorf("Want %v, have %v", now, ToTime(now))
	}
	if ToTime("123.456").IsZero() == false {
		t.Errorf("Want %v, have %v", time.Time{}, ToTime("123.456"))
	}
}

func TestToBool(t *testing.T) {
	if ToBool(true) == false {
		t.Error("Want true")
	}
	if ToBool(33) != false {
		t.Error("Want false")
	}
}
<commit_msg>fix test<commit_after>package dbf

import (
	"testing"
	"time"
)

func TestToFloat64(t *testing.T) {
	if ToFloat64(123.456) != float64(123.456) {
		t.Errorf("Want %f, have %f", float64(123.456), ToFloat64(123.456))
	}
	if ToFloat64("123.456") != float64(0) {
		t.Errorf("Want %f, have %f", 0.0, ToFloat64(123.456))
	}
}

func TestToInt64(t *testing.T) {
	if ToInt64(int64(123456)) != int64(123456) {
		t.Errorf("Want %d, have %d", int64(123456), ToInt64(123456))
	}
	if ToInt64("123.456") != int64(0) {
		t.Errorf("Want %d, have %d", 0, ToInt64(123456))
	}
}

func TestToString(t *testing.T) {
	if ToString("Hêllo!") != "Hêllo!" {
		t.Errorf("Want %q, have %q", "Hêllo!", ToString("Hêllo!"))
	}
	if ToString(123.456) != "" {
		t.Errorf("Want %q, have %q", "", ToString(123.456))
	}
}

func TestToTrimmedString(t *testing.T) {
	if ToTrimmedString("Hêllo!      ") != "Hêllo!" {
		t.Errorf("Want %q, have %q", "Hêllo!", ToTrimmedString("Hêllo!    "))
	}
	if ToTrimmedString(123.456) != "" {
		t.Errorf("Want %q, have %q", "", ToTrimmedString(123.456))
	}
}

func TestToTime(t *testing.T) {
	now := time.Now()
	if ToTime(now).Equal(now) == false {
		t.Errorf("Want %v, have %v", now, ToTime(now))
	}
	if ToTime("123.456").IsZero() == false {
		t.Errorf("Want %v, have %v", time.Time{}, ToTime("123.456"))
	}
}

func TestToBool(t *testing.T) {
	if ToBool(true) == false {
		t.Error("Want true")
	}
	if ToBool(33) != false {
		t.Error("Want false")
	}
}
<|endoftext|>


<commit_before>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package cast

import (
	"html/template"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestToInt(t *testing.T) {
	var eight interface{} = 8
	assert.Equal(t, ToInt(8), 8)
	assert.Equal(t, ToInt(8.31), 8)
	assert.Equal(t, ToInt("8"), 8)
	assert.Equal(t, ToInt(true), 1)
	assert.Equal(t, ToInt(false), 0)
	assert.Equal(t, ToInt(eight), 8)
}

func TestToInt64(t *testing.T) {
	var eight interface{} = 8
	assert.Equal(t, ToInt64(int64(8)), int64(8))
	assert.Equal(t, ToInt64(8), int64(8))
	assert.Equal(t, ToInt64(8.31), int64(8))
	assert.Equal(t, ToInt64("8"), int64(8))
	assert.Equal(t, ToInt64(true), int64(1))
	assert.Equal(t, ToInt64(false), int64(0))
	assert.Equal(t, ToInt64(eight), int64(8))
}

func TestToFloat64(t *testing.T) {
	var eight interface{} = 8
	assert.Equal(t, ToFloat64(8), 8.00)
	assert.Equal(t, ToFloat64(8.31), 8.31)
	assert.Equal(t, ToFloat64("8.31"), 8.31)
	assert.Equal(t, ToFloat64(eight), 8.0)
}

func TestToString(t *testing.T) {
	var foo interface{} = "one more time"
	assert.Equal(t, ToString(8), "8")
	assert.Equal(t, ToString(int64(16)), "16")
	assert.Equal(t, ToString(8.12), "8.12")
	assert.Equal(t, ToString([]byte("one time")), "one time")
	assert.Equal(t, ToString(template.HTML("one time")), "one time")
	assert.Equal(t, ToString(template.URL("http://somehost.foo")), "http://somehost.foo")
	assert.Equal(t, ToString(template.JS("(1+2)")), "(1+2)")
	assert.Equal(t, ToString(template.CSS("a")), "a")
	assert.Equal(t, ToString(template.HTMLAttr("a")), "a")
	assert.Equal(t, ToString(foo), "one more time")
	assert.Equal(t, ToString(nil), "")
	assert.Equal(t, ToString(true), "true")
	assert.Equal(t, ToString(false), "false")
}

type foo struct {
	val string
}

func (x foo) String() string {
	return x.val
}

func TestStringerToString(t *testing.T) {

	var x foo
	x.val = "bar"
	assert.Equal(t, "bar", ToString(x))
}

type fu struct {
	val string
}

func (x fu) Error() string {
	return x.val
}

func TestErrorToString(t *testing.T) {
	var x fu
	x.val = "bar"
	assert.Equal(t, "bar", ToString(x))
}

func TestMaps(t *testing.T) {
	var taxonomies = map[interface{}]interface{}{"tag": "tags", "group": "groups"}
	var stringMapBool = map[interface{}]interface{}{"v1": true, "v2": false}

	// ToStringMapString inputs/outputs
	var stringMapString = map[string]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
	var stringMapInterface = map[string]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
	var interfaceMapString = map[interface{}]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
	var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}

	// ToStringMapStringSlice inputs/outputs
	var stringMapStringSlice = map[string][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}}
	var stringMapInterfaceSlice = map[string][]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}}
	var stringMapStringSingleSliceFieldsResult = map[string][]string{"key 1": []string{"value", "1"}, "key 2": []string{"value", "2"}, "key 3": []string{"value", "3"}}
	var interfaceMapStringSlice = map[interface{}][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}}
	var interfaceMapInterfaceSlice = map[interface{}][]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}}

	var stringMapStringSliceMultiple = map[string][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}}
	var stringMapStringSliceSingle = map[string][]string{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}, "key 3": []string{"value 3"}}

	var stringMapInterface1 = map[string]interface{}{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}}
	var stringMapInterfaceResult1 = map[string][]string{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}}

	assert.Equal(t, ToStringMap(taxonomies), map[string]interface{}{"tag": "tags", "group": "groups"})
	assert.Equal(t, ToStringMapBool(stringMapBool), map[string]bool{"v1": true, "v2": false})

	// ToStringMapString tests
	assert.Equal(t, ToStringMapString(stringMapString), stringMapString)
	assert.Equal(t, ToStringMapString(stringMapInterface), stringMapString)
	assert.Equal(t, ToStringMapString(interfaceMapString), stringMapString)
	assert.Equal(t, ToStringMapString(interfaceMapInterface), stringMapString)

	// ToStringMapStringSlice tests
	assert.Equal(t, ToStringMapStringSlice(stringMapStringSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapInterfaceSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapStringSliceMultiple), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapStringSliceMultiple), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapString), stringMapStringSliceSingle)
	assert.Equal(t, ToStringMapStringSlice(stringMapInterface), stringMapStringSliceSingle)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapStringSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapInterfaceSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapString), stringMapStringSingleSliceFieldsResult)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapInterface), stringMapStringSingleSliceFieldsResult)
	assert.Equal(t, ToStringMapStringSlice(stringMapInterface1), stringMapInterfaceResult1)
}

func TestSlices(t *testing.T) {
	assert.Equal(t, []string{"a", "b"}, ToStringSlice([]string{"a", "b"}))
	assert.Equal(t, []string{"1", "3"}, ToStringSlice([]interface{}{1, 3}))
	assert.Equal(t, []int{1, 3}, ToIntSlice([]int{1, 3}))
	assert.Equal(t, []int{1, 3}, ToIntSlice([]interface{}{1.2, 3.2}))
	assert.Equal(t, []int{2, 3}, ToIntSlice([]string{"2", "3"}))
	assert.Equal(t, []int{2, 3}, ToIntSlice([2]string{"2", "3"}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]bool{true, false, true}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]interface{}{true, false, true}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]int{1,0,1}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]string{"true","false","true"}))
}

func TestToBool(t *testing.T) {
	assert.Equal(t, ToBool(0), false)
	assert.Equal(t, ToBool(nil), false)
	assert.Equal(t, ToBool("false"), false)
	assert.Equal(t, ToBool("FALSE"), false)
	assert.Equal(t, ToBool("False"), false)
	assert.Equal(t, ToBool("f"), false)
	assert.Equal(t, ToBool("F"), false)
	assert.Equal(t, ToBool(false), false)
	assert.Equal(t, ToBool("foo"), false)

	assert.Equal(t, ToBool("true"), true)
	assert.Equal(t, ToBool("TRUE"), true)
	assert.Equal(t, ToBool("True"), true)
	assert.Equal(t, ToBool("t"), true)
	assert.Equal(t, ToBool("T"), true)
	assert.Equal(t, ToBool(1), true)
	assert.Equal(t, ToBool(true), true)
	assert.Equal(t, ToBool(-1), true)
}

func TestIndirectPointers(t *testing.T) {
	x := 13
	y := &x
	z := &y

	assert.Equal(t, ToInt(y), 13)
	assert.Equal(t, ToInt(z), 13)
}

func TestToDuration(t *testing.T) {
	var td time.Duration = 5
	tests := []struct {
		input    interface{}
		expected time.Duration
	}{
		{time.Duration(5), td},
		{int64(5), td},
		{int32(5), td},
		{int16(5), td},
		{int8(5), td},
		{int(5), td},
		{float64(5), td},
		{float32(5), td},
		{string("5"), td},
		{string("5ns"), td},
		{string("5us"), time.Microsecond * td},
		{string("5µs"), time.Microsecond * td},
		{string("5ms"), time.Millisecond * td},
		{string("5s"), time.Second * td},
		{string("5m"), time.Minute * td},
		{string("5h"), time.Hour * td},
	}
	for _, v := range tests {
		assert.Equal(t, v.expected, ToDuration(v.input))
	}
}
<commit_msg>Add benchmark for ToBool<commit_after>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package cast

import (
	"html/template"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestToInt(t *testing.T) {
	var eight interface{} = 8
	assert.Equal(t, ToInt(8), 8)
	assert.Equal(t, ToInt(8.31), 8)
	assert.Equal(t, ToInt("8"), 8)
	assert.Equal(t, ToInt(true), 1)
	assert.Equal(t, ToInt(false), 0)
	assert.Equal(t, ToInt(eight), 8)
}

func TestToInt64(t *testing.T) {
	var eight interface{} = 8
	assert.Equal(t, ToInt64(int64(8)), int64(8))
	assert.Equal(t, ToInt64(8), int64(8))
	assert.Equal(t, ToInt64(8.31), int64(8))
	assert.Equal(t, ToInt64("8"), int64(8))
	assert.Equal(t, ToInt64(true), int64(1))
	assert.Equal(t, ToInt64(false), int64(0))
	assert.Equal(t, ToInt64(eight), int64(8))
}

func TestToFloat64(t *testing.T) {
	var eight interface{} = 8
	assert.Equal(t, ToFloat64(8), 8.00)
	assert.Equal(t, ToFloat64(8.31), 8.31)
	assert.Equal(t, ToFloat64("8.31"), 8.31)
	assert.Equal(t, ToFloat64(eight), 8.0)
}

func TestToString(t *testing.T) {
	var foo interface{} = "one more time"
	assert.Equal(t, ToString(8), "8")
	assert.Equal(t, ToString(int64(16)), "16")
	assert.Equal(t, ToString(8.12), "8.12")
	assert.Equal(t, ToString([]byte("one time")), "one time")
	assert.Equal(t, ToString(template.HTML("one time")), "one time")
	assert.Equal(t, ToString(template.URL("http://somehost.foo")), "http://somehost.foo")
	assert.Equal(t, ToString(template.JS("(1+2)")), "(1+2)")
	assert.Equal(t, ToString(template.CSS("a")), "a")
	assert.Equal(t, ToString(template.HTMLAttr("a")), "a")
	assert.Equal(t, ToString(foo), "one more time")
	assert.Equal(t, ToString(nil), "")
	assert.Equal(t, ToString(true), "true")
	assert.Equal(t, ToString(false), "false")
}

type foo struct {
	val string
}

func (x foo) String() string {
	return x.val
}

func TestStringerToString(t *testing.T) {

	var x foo
	x.val = "bar"
	assert.Equal(t, "bar", ToString(x))
}

type fu struct {
	val string
}

func (x fu) Error() string {
	return x.val
}

func TestErrorToString(t *testing.T) {
	var x fu
	x.val = "bar"
	assert.Equal(t, "bar", ToString(x))
}

func TestMaps(t *testing.T) {
	var taxonomies = map[interface{}]interface{}{"tag": "tags", "group": "groups"}
	var stringMapBool = map[interface{}]interface{}{"v1": true, "v2": false}

	// ToStringMapString inputs/outputs
	var stringMapString = map[string]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
	var stringMapInterface = map[string]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
	var interfaceMapString = map[interface{}]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}
	var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}

	// ToStringMapStringSlice inputs/outputs
	var stringMapStringSlice = map[string][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}}
	var stringMapInterfaceSlice = map[string][]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}}
	var stringMapStringSingleSliceFieldsResult = map[string][]string{"key 1": []string{"value", "1"}, "key 2": []string{"value", "2"}, "key 3": []string{"value", "3"}}
	var interfaceMapStringSlice = map[interface{}][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}}
	var interfaceMapInterfaceSlice = map[interface{}][]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}}

	var stringMapStringSliceMultiple = map[string][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}}
	var stringMapStringSliceSingle = map[string][]string{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}, "key 3": []string{"value 3"}}

	var stringMapInterface1 = map[string]interface{}{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}}
	var stringMapInterfaceResult1 = map[string][]string{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}}

	assert.Equal(t, ToStringMap(taxonomies), map[string]interface{}{"tag": "tags", "group": "groups"})
	assert.Equal(t, ToStringMapBool(stringMapBool), map[string]bool{"v1": true, "v2": false})

	// ToStringMapString tests
	assert.Equal(t, ToStringMapString(stringMapString), stringMapString)
	assert.Equal(t, ToStringMapString(stringMapInterface), stringMapString)
	assert.Equal(t, ToStringMapString(interfaceMapString), stringMapString)
	assert.Equal(t, ToStringMapString(interfaceMapInterface), stringMapString)

	// ToStringMapStringSlice tests
	assert.Equal(t, ToStringMapStringSlice(stringMapStringSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapInterfaceSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapStringSliceMultiple), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapStringSliceMultiple), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(stringMapString), stringMapStringSliceSingle)
	assert.Equal(t, ToStringMapStringSlice(stringMapInterface), stringMapStringSliceSingle)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapStringSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapInterfaceSlice), stringMapStringSlice)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapString), stringMapStringSingleSliceFieldsResult)
	assert.Equal(t, ToStringMapStringSlice(interfaceMapInterface), stringMapStringSingleSliceFieldsResult)
	assert.Equal(t, ToStringMapStringSlice(stringMapInterface1), stringMapInterfaceResult1)
}

func TestSlices(t *testing.T) {
	assert.Equal(t, []string{"a", "b"}, ToStringSlice([]string{"a", "b"}))
	assert.Equal(t, []string{"1", "3"}, ToStringSlice([]interface{}{1, 3}))
	assert.Equal(t, []int{1, 3}, ToIntSlice([]int{1, 3}))
	assert.Equal(t, []int{1, 3}, ToIntSlice([]interface{}{1.2, 3.2}))
	assert.Equal(t, []int{2, 3}, ToIntSlice([]string{"2", "3"}))
	assert.Equal(t, []int{2, 3}, ToIntSlice([2]string{"2", "3"}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]bool{true, false, true}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]interface{}{true, false, true}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]int{1, 0, 1}))
	assert.Equal(t, []bool{true, false, true}, ToBoolSlice([]string{"true", "false", "true"}))
}

func TestToBool(t *testing.T) {
	assert.Equal(t, ToBool(0), false)
	assert.Equal(t, ToBool(nil), false)
	assert.Equal(t, ToBool("false"), false)
	assert.Equal(t, ToBool("FALSE"), false)
	assert.Equal(t, ToBool("False"), false)
	assert.Equal(t, ToBool("f"), false)
	assert.Equal(t, ToBool("F"), false)
	assert.Equal(t, ToBool(false), false)
	assert.Equal(t, ToBool("foo"), false)

	assert.Equal(t, ToBool("true"), true)
	assert.Equal(t, ToBool("TRUE"), true)
	assert.Equal(t, ToBool("True"), true)
	assert.Equal(t, ToBool("t"), true)
	assert.Equal(t, ToBool("T"), true)
	assert.Equal(t, ToBool(1), true)
	assert.Equal(t, ToBool(true), true)
	assert.Equal(t, ToBool(-1), true)
}

func BenchmarkTooBool(b *testing.B) {
	for i := 0; i < b.N; i++ {
		if !ToBool(true) {
			b.Fatal("ToBool returned false")
		}
	}
}

func TestIndirectPointers(t *testing.T) {
	x := 13
	y := &x
	z := &y

	assert.Equal(t, ToInt(y), 13)
	assert.Equal(t, ToInt(z), 13)
}

func TestToDuration(t *testing.T) {
	var td time.Duration = 5
	tests := []struct {
		input    interface{}
		expected time.Duration
	}{
		{time.Duration(5), td},
		{int64(5), td},
		{int32(5), td},
		{int16(5), td},
		{int8(5), td},
		{int(5), td},
		{float64(5), td},
		{float32(5), td},
		{string("5"), td},
		{string("5ns"), td},
		{string("5us"), time.Microsecond * td},
		{string("5µs"), time.Microsecond * td},
		{string("5ms"), time.Millisecond * td},
		{string("5s"), time.Second * td},
		{string("5m"), time.Minute * td},
		{string("5h"), time.Hour * td},
	}
	for _, v := range tests {
		assert.Equal(t, v.expected, ToDuration(v.input))
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goapp

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"fmt"
	"github.com/mjibson/goon"
	"net/http"
	"time"
)

func init() {
	http.HandleFunc("/", Main)
}

func Main(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	n := goon.NewGoon(r)

	// key tests

	noid := NoId{}
	if _, err := n.Key(noid); err == nil {
		fmt.Fprintln(w, "expected error on noid")
	}

	var keyTests = []keyTest{
		keyTest{
			HasId{Id: 1},
			datastore.NewKey(c, "HasId", "", 1, nil),
		},
		keyTest{
			HasKind{Id: 1, Kind: "OtherKind"},
			datastore.NewKey(c, "OtherKind", "", 1, nil),
		},
		keyTest{
			HasDefaultKind{Id: 1, Kind: "OtherKind"},
			datastore.NewKey(c, "OtherKind", "", 1, nil),
		},
		keyTest{
			HasDefaultKind{Id: 1},
			datastore.NewKey(c, "DefaultKind", "", 1, nil),
		},
	}

	for _, kt := range keyTests {
		if k, err := n.Key(kt.obj); err != nil {
			fmt.Fprintln(w, "error:", err.Error())
		} else if !k.Equal(kt.key) {
			fmt.Fprintln(w, "keys not equal")
		}
	}

	// datastore tests

	initTest(c)
	if err := n.Get(&HasId{Id: 0}); err == nil {
		fmt.Fprintln(w, "ds: expected error")
	}
	if err := n.Get(&HasId{Id: 1}); err != datastore.ErrNoSuchEntity {
		fmt.Fprintln(w, "ds: expected no such entity")
	}
	// run twice to make sure autocaching works correctly
	if err := n.Get(&HasId{Id: 1}); err != datastore.ErrNoSuchEntity {
		fmt.Fprintln(w, "ds: expected no such entity")
	}
	es := []*HasId{
		{Id: 1, Name: "one"},
		{Id: 2, Name: "two"},
	}
	nes := []*HasId{
		{Id: 1},
		{Id: 2},
	}
	if err := n.GetMulti(es); err == nil {
		fmt.Fprintln(w, "ds: expected error")
	} else if !goon.NotFound(err, 0) {
		fmt.Fprintln(w, "ds: not found error 0")
	} else if !goon.NotFound(err, 1) {
		fmt.Fprintln(w, "ds: not found error 1")
	} else if goon.NotFound(err, 2) {
		fmt.Fprintln(w, "ds: not found error 2")
	}
	if err := n.PutMulti(es); err != nil {
		fmt.Fprintln(w, "put: unexpected error")
	}
	if err := n.GetMulti(nes); err != nil {
		fmt.Fprintln(w, "put: unexpected error")
	} else if es[0] != nes[0] || es[1] != nes[1] {
		fmt.Fprintln(w, "put: bad results")
	} else {
		nesk0, _ := n.Key(nes[0])
		if !nesk0.Equal(datastore.NewKey(c, "HasId", "", 1, nil)) {
			fmt.Fprintln(w, "put: bad key")
		}
		nesk1, _ := n.Key(nes[1])
		if !nesk1.Equal(datastore.NewKey(c, "HasId", "", 2, nil)) {
			fmt.Fprintln(w, "put: bad key")
		}
	}
	// force partial fetch from memcache and then datastore
	memcache.Flush(c)
	if err := n.Get(nes[0]); err != nil {
		fmt.Fprintln(w, "get: unexpected error")
	}
	if err := n.GetMulti(nes); err != nil {
		fmt.Fprintln(w, "get: unexpected error")
	}

	fmt.Fprintln(w, "done", time.Now())
}

func initTest(c appengine.Context) {
	keys, _ := datastore.NewQuery("HasId").KeysOnly().GetAll(c, nil)
	datastore.DeleteMulti(c, keys)
	memcache.Flush(c)
}

type keyTest struct {
	obj interface{}
	key *datastore.Key
}

type NoId struct {
}

type HasId struct {
	Id   int64 `datastore:"-" goon:"id"`
	Name string
}

type HasKind struct {
	Id   int64  `datastore:"-" goon:"id"`
	Kind string `datastore:"-" goon:"kind"`
	Name string
}

type HasDefaultKind struct {
	Id   int64  `datastore:"-" goon:"id"`
	Kind string `datastore:"-" goon:"kind,DefaultKind"`
	Name string
}
<commit_msg>Update tests<commit_after>/*
 * Copyright (c) 2013 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goapp

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"fmt"
	"github.com/mjibson/goon"
	"net/http"
	"time"
)

func init() {
	http.HandleFunc("/", Main)
}

func Main(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	n := goon.NewGoon(r)

	// key tests

	noid := NoId{}
	if k, err := n.KeyError(noid); err != nil || !k.Incomplete() {
		fmt.Fprintln(w, "expected incomplete on noid")
	}

	var keyTests = []keyTest{
		keyTest{
			HasId{Id: 1},
			datastore.NewKey(c, "HasId", "", 1, nil),
		},
		keyTest{
			HasKind{Id: 1, Kind: "OtherKind"},
			datastore.NewKey(c, "OtherKind", "", 1, nil),
		},
		keyTest{
			HasDefaultKind{Id: 1, Kind: "OtherKind"},
			datastore.NewKey(c, "OtherKind", "", 1, nil),
		},
		keyTest{
			HasDefaultKind{Id: 1},
			datastore.NewKey(c, "DefaultKind", "", 1, nil),
		},
	}

	for _, kt := range keyTests {
		if k, err := n.KeyError(kt.obj); err != nil {
			fmt.Fprintln(w, "error:", err.Error())
		} else if !k.Equal(kt.key) {
			fmt.Fprintln(w, "keys not equal")
		}
	}

	// datastore tests

	initTest(c)
	if err := n.Get(&HasId{Id: 0}); err == nil {
		fmt.Fprintln(w, "ds: expected error")
	}
	if err := n.Get(&HasId{Id: 1}); err != datastore.ErrNoSuchEntity {
		fmt.Fprintln(w, "ds: expected no such entity")
	}
	// run twice to make sure autocaching works correctly
	if err := n.Get(&HasId{Id: 1}); err != datastore.ErrNoSuchEntity {
		fmt.Fprintln(w, "ds: expected no such entity")
	}
	es := []*HasId{
		{Id: 1, Name: "one"},
		{Id: 2, Name: "two"},
	}
	nes := []*HasId{
		{Id: 1},
		{Id: 2},
	}
	if err := n.GetMulti(es); err == nil {
		fmt.Fprintln(w, "ds: expected error")
	} else if !goon.NotFound(err, 0) {
		fmt.Fprintln(w, "ds: not found error 0")
	} else if !goon.NotFound(err, 1) {
		fmt.Fprintln(w, "ds: not found error 1")
	} else if goon.NotFound(err, 2) {
		fmt.Fprintln(w, "ds: not found error 2")
	}
	if err := n.PutMulti(es); err != nil {
		fmt.Fprintln(w, "put: unexpected error")
	}
	if err := n.GetMulti(nes); err != nil {
		fmt.Fprintln(w, "put: unexpected error")
	} else if es[0] != nes[0] || es[1] != nes[1] {
		fmt.Fprintln(w, "put: bad results")
	} else {
		nesk0 := n.Key(nes[0])
		if !nesk0.Equal(datastore.NewKey(c, "HasId", "", 1, nil)) {
			fmt.Fprintln(w, "put: bad key")
		}
		nesk1 := n.Key(nes[1])
		if !nesk1.Equal(datastore.NewKey(c, "HasId", "", 2, nil)) {
			fmt.Fprintln(w, "put: bad key")
		}
	}
	// force partial fetch from memcache and then datastore
	memcache.Flush(c)
	if err := n.Get(nes[0]); err != nil {
		fmt.Fprintln(w, "get: unexpected error")
	}
	if err := n.GetMulti(nes); err != nil {
		fmt.Fprintln(w, "get: unexpected error")
	}

	fmt.Fprintln(w, "done", time.Now())
}

func initTest(c appengine.Context) {
	keys, _ := datastore.NewQuery("HasId").KeysOnly().GetAll(c, nil)
	datastore.DeleteMulti(c, keys)
	memcache.Flush(c)
}

type keyTest struct {
	obj interface{}
	key *datastore.Key
}

type NoId struct {
}

type HasId struct {
	Id   int64 `datastore:"-" goon:"id"`
	Name string
}

type HasKind struct {
	Id   int64  `datastore:"-" goon:"id"`
	Kind string `datastore:"-" goon:"kind"`
	Name string
}

type HasDefaultKind struct {
	Id   int64  `datastore:"-" goon:"id"`
	Kind string `datastore:"-" goon:"kind,DefaultKind"`
	Name string
}
<|endoftext|>


<commit_before>package vaddy

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"time"
)

const SUCCESS_EXIT int = 0
const ERROR_EXIT int = 1
const LIMIT_WAIT_COUNT int = 540 // 20sec * 540 = 3 hours
const API_SERVER string = "https://api.vaddy.net"

type StartScan struct {
	ScanID string `json:"scan_id"`
}

type ScanResult struct {
	Status        string `json:"status"`
	AlertCount    int    `json:"alert_count"`
	ScanResultUrl string `json:"scan_result_url"`
}

func main() {
	fmt.Println("==== Start VAddy Scan ====")

	var auth_key, user, fqdn, crawl string = getApiParamsFromArgsOrEnv()

	scan_id := startScan(auth_key, user, fqdn, crawl)

	var wait_count int = 0
	for {
		checkScanResult(auth_key, user, fqdn, scan_id)

		time.Sleep(20 * time.Second) //wait 20 second
		wait_count++
		if wait_count > LIMIT_WAIT_COUNT {
			fmt.Println("Error: time out")
			os.Exit(ERROR_EXIT)
		}
	}
}

func getApiParamsFromArgsOrEnv() (string, string, string, string) {
	var auth_key, user, fqdn, crawl string
	if len(os.Args) < 4 {
		return getArgsFromEnv()
	}

	auth_key = os.Args[1]
	user = os.Args[2]
	fqdn = os.Args[3]
	if len(os.Args) >= 5 {
		crawl = os.Args[4]
	}
	return auth_key, user, fqdn, crawl
}

func getArgsFromEnv() (string, string, string, string) {
	var auth_key, user, fqdn, crawl string
	auth_key, ok1 := os.LookupEnv("VADDY_TOKEN")
	user, ok2 := os.LookupEnv("VADDY_USER")
	fqdn, ok3 := os.LookupEnv("VADDY_HOST")
	crawl, _ = os.LookupEnv("VADDY_CRAWL")

	if !ok1 || !ok2 || !ok3 {
		fmt.Println("Missing arguments or system env.")
		fmt.Println("USAGE: vaddy ApiKey UserId FQDN CrawlID(optional)")
		os.Exit(ERROR_EXIT)
	}
	return auth_key, user, fqdn, crawl
}

func startScan(auth_key string, user string, fqdn string, crawl string) string {
	values := url.Values{}
	values.Add("auth_key", auth_key)
	values.Add("user", user)
	values.Add("fqdn", fqdn)
	values.Add("action", "start")
	if len(crawl) > 0 {
		values.Add("crawl_id", crawl)
	}

	apiServer := getApiServerName()
	res, err := http.PostForm(apiServer+"/v1/scan", values)
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
	defer res.Body.Close()
	json_response := getResponseData(res)
	scanId := getScanId(json_response)
	//fmt.Println("scanId: " + scanId)
	return scanId
}

func getScanResult(auth_key string, user string, fqdn string, scan_id string) []byte {
	values := url.Values{}
	values.Add("auth_key", auth_key)
	values.Add("user", user)
	values.Add("fqdn", fqdn)
	values.Add("scan_id", scan_id)

	apiServer := getApiServerName()
	res, err := http.Get(apiServer + "/v1/scan/result?" + values.Encode())
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
	defer res.Body.Close()

	json_response := getResponseData(res)
	return json_response
}

func checkScanResult(auth_key string, user string, fqdn string, scan_id string) {
	json_response := getScanResult(auth_key, user, fqdn, scan_id)

	var scan_result ScanResult
	convertJsonToStruct(json_response, &scan_result)

	status := scan_result.Status
	switch status {
	case "scanning":
		fmt.Println(scan_result.Status)
	case "canceled":
		fmt.Println(scan_result.Status)
		os.Exit(ERROR_EXIT)
	case "finish":
		//fmt.Println(string(json_response) + "\n")
		fmt.Println("Server: " + fqdn)
		fmt.Println("scanId: " + scan_id)
		fmt.Println("Result URL: " + scan_result.ScanResultUrl)

		if scan_result.AlertCount > 0 {
			fmt.Print("Vulnerabilities: ")
			fmt.Println(scan_result.AlertCount)
			fmt.Println("Warning!!!")
			os.Exit(ERROR_EXIT)
		} else {
			fmt.Println("Scan Success. No vulnerabilities!")
			os.Exit(SUCCESS_EXIT)
		}
	}
}

func getResponseData(resp *http.Response) []byte {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(string(body))
		os.Exit(ERROR_EXIT)
	}
	status_code := resp.StatusCode
	//fmt.Println(status_code)
	if status_code != 200 {
		fmt.Println("Network/Auth error\n" + string(body))
		os.Exit(ERROR_EXIT)
	}
	return []byte(body)
}

func getScanId(jsonByteData []byte) string {
	var scan_result StartScan
	convertJsonToStruct(jsonByteData, &scan_result)
	return scan_result.ScanID
}

func convertJsonToStruct(jsonByteData []byte, structData interface{}) {
	err := json.Unmarshal(jsonByteData, structData)
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
}

func getApiServerName() string {
	apiserver, ok := os.LookupEnv("VADDY_API_SERVER")
	if ok {
		return apiserver
	}
	return API_SERVER
}
<commit_msg>need to define main for package name<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"time"
)

const SUCCESS_EXIT int = 0
const ERROR_EXIT int = 1
const LIMIT_WAIT_COUNT int = 540 // 20sec * 540 = 3 hours
const API_SERVER string = "https://api.vaddy.net"

type StartScan struct {
	ScanID string `json:"scan_id"`
}

type ScanResult struct {
	Status        string `json:"status"`
	AlertCount    int    `json:"alert_count"`
	ScanResultUrl string `json:"scan_result_url"`
}

func main() {
	fmt.Println("==== Start VAddy Scan ====")

	var auth_key, user, fqdn, crawl string = getApiParamsFromArgsOrEnv()

	scan_id := startScan(auth_key, user, fqdn, crawl)

	var wait_count int = 0
	for {
		checkScanResult(auth_key, user, fqdn, scan_id)

		time.Sleep(20 * time.Second) //wait 20 second
		wait_count++
		if wait_count > LIMIT_WAIT_COUNT {
			fmt.Println("Error: time out")
			os.Exit(ERROR_EXIT)
		}
	}
}

func getApiParamsFromArgsOrEnv() (string, string, string, string) {
	var auth_key, user, fqdn, crawl string
	if len(os.Args) < 4 {
		return getArgsFromEnv()
	}

	auth_key = os.Args[1]
	user = os.Args[2]
	fqdn = os.Args[3]
	if len(os.Args) >= 5 {
		crawl = os.Args[4]
	}
	return auth_key, user, fqdn, crawl
}

func getArgsFromEnv() (string, string, string, string) {
	var auth_key, user, fqdn, crawl string
	auth_key, ok1 := os.LookupEnv("VADDY_TOKEN")
	user, ok2 := os.LookupEnv("VADDY_USER")
	fqdn, ok3 := os.LookupEnv("VADDY_HOST")
	crawl, _ = os.LookupEnv("VADDY_CRAWL")

	if !ok1 || !ok2 || !ok3 {
		fmt.Println("Missing arguments or system env.")
		fmt.Println("USAGE: vaddy ApiKey UserId FQDN CrawlID(optional)")
		os.Exit(ERROR_EXIT)
	}
	return auth_key, user, fqdn, crawl
}

func startScan(auth_key string, user string, fqdn string, crawl string) string {
	values := url.Values{}
	values.Add("auth_key", auth_key)
	values.Add("user", user)
	values.Add("fqdn", fqdn)
	values.Add("action", "start")
	if len(crawl) > 0 {
		values.Add("crawl_id", crawl)
	}

	apiServer := getApiServerName()
	res, err := http.PostForm(apiServer+"/v1/scan", values)
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
	defer res.Body.Close()
	json_response := getResponseData(res)
	scanId := getScanId(json_response)
	//fmt.Println("scanId: " + scanId)
	return scanId
}

func getScanResult(auth_key string, user string, fqdn string, scan_id string) []byte {
	values := url.Values{}
	values.Add("auth_key", auth_key)
	values.Add("user", user)
	values.Add("fqdn", fqdn)
	values.Add("scan_id", scan_id)

	apiServer := getApiServerName()
	res, err := http.Get(apiServer + "/v1/scan/result?" + values.Encode())
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
	defer res.Body.Close()

	json_response := getResponseData(res)
	return json_response
}

func checkScanResult(auth_key string, user string, fqdn string, scan_id string) {
	json_response := getScanResult(auth_key, user, fqdn, scan_id)

	var scan_result ScanResult
	convertJsonToStruct(json_response, &scan_result)

	status := scan_result.Status
	switch status {
	case "scanning":
		fmt.Println(scan_result.Status)
	case "canceled":
		fmt.Println(scan_result.Status)
		os.Exit(ERROR_EXIT)
	case "finish":
		//fmt.Println(string(json_response) + "\n")
		fmt.Println("Server: " + fqdn)
		fmt.Println("scanId: " + scan_id)
		fmt.Println("Result URL: " + scan_result.ScanResultUrl)

		if scan_result.AlertCount > 0 {
			fmt.Print("Vulnerabilities: ")
			fmt.Println(scan_result.AlertCount)
			fmt.Println("Warning!!!")
			os.Exit(ERROR_EXIT)
		} else {
			fmt.Println("Scan Success. No vulnerabilities!")
			os.Exit(SUCCESS_EXIT)
		}
	}
}

func getResponseData(resp *http.Response) []byte {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(string(body))
		os.Exit(ERROR_EXIT)
	}
	status_code := resp.StatusCode
	//fmt.Println(status_code)
	if status_code != 200 {
		fmt.Println("Network/Auth error\n" + string(body))
		os.Exit(ERROR_EXIT)
	}
	return []byte(body)
}

func getScanId(jsonByteData []byte) string {
	var scan_result StartScan
	convertJsonToStruct(jsonByteData, &scan_result)
	return scan_result.ScanID
}

func convertJsonToStruct(jsonByteData []byte, structData interface{}) {
	err := json.Unmarshal(jsonByteData, structData)
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
}

func getApiServerName() string {
	apiserver, ok := os.LookupEnv("VADDY_API_SERVER")
	if ok {
		return apiserver
	}
	return API_SERVER
}
<|endoftext|>