<commit_before>package svfs

import (
	"fmt"
	"time"
)

var (
	// CacheTimeout represents cache entries timeout.
	CacheTimeout time.Duration
	// CacheMaxEntries represents the cache size.
	CacheMaxEntries int64
	// CacheMaxAccess represents cache entries max access count.
	CacheMaxAccess int64
	changeCache    = NewSimpleCache() // Cache for mutating objects
	directoryCache = NewCache()       // Cache for directories content
)

// Cache holds a map of cache entries. Its size can be configured
// as well as cache entries access limit and expiration time.
type Cache struct {
	content   map[string]*CacheValue
	nodeCount uint64
}

// CacheValue is the representation of a cache entry.
// It tracks expiration date, access count and holds
// a parent node with its children. It can be set
// as temporary, meaning that it will be stored within
// the cache but evicted on first access.
type CacheValue struct {
	date        time.Time
	accessCount uint64
	temporary   bool
	node        Node
	nodes       map[string]Node
}

// NewCache creates a new cache
func NewCache() *Cache {
	return &Cache{
		content: make(map[string]*CacheValue),
	}
}

func (c *Cache) key(container, path string) string {
	return fmt.Sprintf("%s:%s", container, path)
}

// AddAll creates a new cache entry with the key container:path and a map of nodes
// as a value. Node represents the parent node type. If the cache entry count limit is
// reached, it will be marked as temporary thus evicted after one read.
func (c *Cache) AddAll(container, path string, node Node, nodes map[string]Node) {
	entry := &CacheValue{
		date:  time.Now(),
		node:  node,
		nodes: nodes,
	}

	if !(CacheMaxEntries < 0) &&
		(c.nodeCount+uint64(len(nodes)) >= uint64(CacheMaxEntries)) ||
		CacheMaxAccess == 0 {
		entry.temporary = true
	} else {
		c.nodeCount += uint64(len(nodes))
	}

	c.content[c.key(container, path)] = entry
}

// Delete removes a node from cache.
func (c *Cache) Delete(container, path, name string) {
	v, ok := c.content[c.key(container, path)]
	if !ok {
		return
	}
	delete(v.nodes, name)
}

// DeleteAll removes all nodes for the cache key container:path.
func (c *Cache) DeleteAll(container, path string) {
	v, found := c.content[c.key(container, path)]
	if found &&
		!v.temporary {
		c.nodeCount -= uint64(len(c.content[c.key(container, path)].nodes))
		delete(c.content, c.key(container, path))
	}
}

// Get retrieves a specific node from the cache. It returns nil if
// the cache key container:path is missing.
func (c *Cache) Get(container, path, name string) Node {
	v, ok := c.content[c.key(container, path)]
	if !ok {
		return nil
	}
	return v.nodes[name]
}

// GetAll retrieves all nodes for the cache key container:path. It returns
// the parent node and its children nodes. If the cache entry is not found
// or expired or access count exceeds the limit, both values will be nil.
func (c *Cache) GetAll(container, path string) (Node, map[string]Node) {
	v, found := c.content[c.key(container, path)]

	// Not found
	if !found {
		return nil, nil
	}

	// Increase access counter
	v.accessCount++

	// Found but expired
	if time.Now().After(v.date.Add(CacheTimeout)) {
		defer c.DeleteAll(container, path)
		return nil, nil
	}

	if v.temporary ||
		(!(CacheMaxAccess < 0) && v.accessCount == uint64(CacheMaxAccess)) {
		defer c.DeleteAll(container, path)
	}

	return v.node, v.nodes
}

// Peek checks if a valid cache entry belongs to container:path
// key without changing cache access count for this entry.
// Returns the parent node with the result.
func (c *Cache) Peek(container, path string) (Node, bool) {
	v, found := c.content[c.key(container, path)]

	// Not found
	if !found {
		return nil, false
	}

	// Found but expired
	if time.Now().After(v.date.Add(CacheTimeout)) {
		return nil, false
	}

	return v.node, true
}

// Set adds a specific node in cache, given a previous peek
// operation succeeded.
func (c *Cache) Set(container, path, name string, node Node) {
	v, ok := c.content[c.key(container, path)]
	if !ok {
		return
	}
	v.nodes[name] = node
}

// SimpleCache is a simplistic caching implementation
// only relying on a hashmap with basic functions.
type SimpleCache struct {
	changes map[string]Node
}

// NewSimpleCache creates a new simplistic cache.
func NewSimpleCache() *SimpleCache {
	return &SimpleCache{
		changes: make(map[string]Node),
	}
}

func (c *SimpleCache) key(container, path string) string {
	return fmt.Sprintf("%s:%s", container, path)
}

// Add pushes a new cache entry.
func (c *SimpleCache) Add(container, path string, node Node) {
	c.changes[c.key(container, path)] = node
}

// Exist checks whether a cache key exist or not.
func (c *SimpleCache) Exist(container, path string) bool {
	return c.changes[c.key(container, path)] != nil
}

// Get retrieves a cache entry for the given key.
func (c *SimpleCache) Get(container, path string) Node {
	return c.changes[c.key(container, path)]
}

// Remove pops the cache entry at this key.
func (c *SimpleCache) Remove(container, path string) {
	delete(c.changes, c.key(container, path))
}
<commit_msg>Concurrent map access, fixes #74<commit_after>package svfs

import (
	"fmt"
	"sync"
	"time"
)

var (
	// CacheTimeout represents cache entries timeout.
	CacheTimeout time.Duration
	// CacheMaxEntries represents the cache size.
	CacheMaxEntries int64
	// CacheMaxAccess represents cache entries max access count.
	CacheMaxAccess int64
	changeCache    = NewSimpleCache() // Cache for mutating objects
	directoryCache = NewCache()       // Cache for directories content
)

// Cache holds a map of cache entries. Its size can be configured
// as well as cache entries access limit and expiration time.
type Cache struct {
	content   map[string]*CacheValue
	mutex     sync.Mutex
	nodeCount uint64
}

// CacheValue is the representation of a cache entry.
// It tracks expiration date, access count and holds
// a parent node with its children. It can be set
// as temporary, meaning that it will be stored within
// the cache but evicted on first access.
type CacheValue struct {
	date        time.Time
	accessCount uint64
	mutex       sync.Mutex
	temporary   bool
	node        Node
	nodes       map[string]Node
}

// NewCache creates a new cache
func NewCache() *Cache {
	return &Cache{
		content: make(map[string]*CacheValue),
	}
}

func (c *Cache) key(container, path string) string {
	return fmt.Sprintf("%s:%s", container, path)
}

// AddAll creates a new cache entry with the key container:path and a map of nodes
// as a value. Node represents the parent node type. If the cache entry count limit is
// reached, it will be marked as temporary thus evicted after one read.
func (c *Cache) AddAll(container, path string, node Node, nodes map[string]Node) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	entry := &CacheValue{
		date:  time.Now(),
		node:  node,
		nodes: nodes,
	}

	if !(CacheMaxEntries < 0) &&
		(c.nodeCount+uint64(len(nodes)) >= uint64(CacheMaxEntries)) ||
		CacheMaxAccess == 0 {
		entry.temporary = true
	} else {
		c.nodeCount += uint64(len(nodes))
	}

	c.content[c.key(container, path)] = entry
}

// Delete removes a node from cache.
func (c *Cache) Delete(container, path, name string) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	v, ok := c.content[c.key(container, path)]
	if !ok {
		return
	}

	v.mutex.Lock()
	defer v.mutex.Unlock()

	delete(v.nodes, name)
}

// DeleteAll removes all nodes for the cache key container:path.
func (c *Cache) DeleteAll(container, path string) {
	c.deleteAll(container, path, true)
}

func (c *Cache) deleteAll(container, path string, lock bool) {
	if lock {
		c.mutex.Lock()
		defer c.mutex.Unlock()
	}

	v, found := c.content[c.key(container, path)]

	if lock {
		v.mutex.Lock()
		defer v.mutex.Unlock()
	}

	if found &&
		!v.temporary {
		c.nodeCount -= uint64(len(c.content[c.key(container, path)].nodes))
		delete(c.content, c.key(container, path))
	}
}

// Get retrieves a specific node from the cache. It returns nil if
// the cache key container:path is missing.
func (c *Cache) Get(container, path, name string) Node {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	v, ok := c.content[c.key(container, path)]
	if !ok {
		return nil
	}

	v.mutex.Lock()
	defer v.mutex.Unlock()

	return v.nodes[name]
}

// GetAll retrieves all nodes for the cache key container:path. It returns
// the parent node and its children nodes. If the cache entry is not found
// or expired or access count exceeds the limit, both values will be nil.
func (c *Cache) GetAll(container, path string) (Node, map[string]Node) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	v, found := c.content[c.key(container, path)]

	// Not found
	if !found {
		return nil, nil
	}

	v.mutex.Lock()
	defer v.mutex.Unlock()

	// Increase access counter
	v.accessCount++

	// Found but expired
	if time.Now().After(v.date.Add(CacheTimeout)) {
		defer c.deleteAll(container, path, false)
		return nil, nil
	}

	if v.temporary ||
		(!(CacheMaxAccess < 0) && v.accessCount == uint64(CacheMaxAccess)) {
		defer c.deleteAll(container, path, false)
	}

	return v.node, v.nodes
}

// Peek checks if a valid cache entry belongs to container:path
// key without changing cache access count for this entry.
// Returns the parent node with the result.
func (c *Cache) Peek(container, path string) (Node, bool) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	v, found := c.content[c.key(container, path)]

	// Not found
	if !found {
		return nil, false
	}

	v.mutex.Lock()
	defer v.mutex.Unlock()

	// Found but expired
	if time.Now().After(v.date.Add(CacheTimeout)) {
		return nil, false
	}

	return v.node, true
}

// Set adds a specific node in cache, given a previous peek
// operation succeeded.
func (c *Cache) Set(container, path, name string, node Node) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	v, ok := c.content[c.key(container, path)]
	if !ok {
		return
	}

	v.mutex.Lock()
	defer v.mutex.Unlock()

	v.nodes[name] = node
}

// SimpleCache is a simplistic caching implementation
// only relying on a hashmap with basic functions.
type SimpleCache struct {
	changes map[string]Node
}

// NewSimpleCache creates a new simplistic cache.
func NewSimpleCache() *SimpleCache {
	return &SimpleCache{
		changes: make(map[string]Node),
	}
}

func (c *SimpleCache) key(container, path string) string {
	return fmt.Sprintf("%s:%s", container, path)
}

// Add pushes a new cache entry.
func (c *SimpleCache) Add(container, path string, node Node) {
	c.changes[c.key(container, path)] = node
}

// Exist checks whether a cache key exist or not.
func (c *SimpleCache) Exist(container, path string) bool {
	return c.changes[c.key(container, path)] != nil
}

// Get retrieves a cache entry for the given key.
func (c *SimpleCache) Get(container, path string) Node {
	return c.changes[c.key(container, path)]
}

// Remove pops the cache entry at this key.
func (c *SimpleCache) Remove(container, path string) {
	delete(c.changes, c.key(container, path))
}
<|endoftext|>


<commit_before>package main

import (
	. "fmt"
	"os"
	"raft/follower"
	// "raft/leader"
	"raft/settings"
	"strconv"
	"time"
	"udp"
)

const (
	maxTimeout = time.Second * 3
	envVar     = "RAFT_PORT"
)

var isLeader bool = false

func init() {

	index, _ := strconv.Atoi(os.Args[1])

	settings.SetMyPort(settings.Cluster[index])

	udp.SetRecvPort(settings.Port)

	udp.SetTimeout(time.Now().Add(maxTimeout))

	if index == 0 {
		isLeader = true
	}

	Printf("My port is %s\n", settings.Port)

}

func main() {

	var msg string

	for {
		if isLeader {

			listening := true

			ch := make(chan string, 10)

			for {

				for _, i := range settings.Cluster {
					if i != settings.Port {

						go udp.Send("heartbeat", i)

					}
				}

				Println("Sent heartbeats to cluster")

				for _, i := range settings.Cluster {

					countResponse := 0

					select {
					case msg = <-ch:
						if msg == "alive" {
							countResponse++
						}
					case <-time.After(time.Second):
						break
					}

				}

			}

			// time.Sleep(50 * time.Millisecond)

		} else {

			msg = udp.ReceiveTimeout()

			follower.HandleRequest(msg)

		}

	}
}
<commit_msg>Working on leader<commit_after>package main

import (
	. "fmt"
	"os"
	"raft/follower"
	// "raft/leader"
	"raft/settings"
	"strconv"
	"time"
	"udp"
)

const (
	maxTimeout = time.Second * 3
	envVar     = "RAFT_PORT"
)

var isLeader bool = false

func init() {

	index, _ := strconv.Atoi(os.Args[1])

	settings.SetMyPort(settings.Cluster[index])

	udp.SetRecvPort(settings.Port)

	udp.SetTimeout(time.Now().Add(maxTimeout))

	if index == 0 {
		isLeader = true
	}

	Printf("My port is %s\n", settings.Port)

}

func main() {

	var msg string

	for {

		if isLeader {

			listening := true

			ch := make(chan string, 10)

			for {

				for _, i := range settings.Cluster {
					if i != settings.Port {

						go udp.Send("heartbeat", i)

					}
				}

				Println("Sent heartbeats to cluster")

				for _, i := range settings.Cluster {

					countResponse := 0

					select {
					case msg = <-ch:
						if msg == "alive" {
							countResponse++
						}
					case <-time.After(time.Second):
						break
					}

				}

			}

			// time.Sleep(50 * time.Millisecond)

		} else {

			msg = udp.ReceiveTimeout()

			follower.HandleRequest(msg)

		}

	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io"

	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
	"github.com/ipfs/go-ipfs/core/coreunix"

	cmds "github.com/ipfs/go-ipfs/commands"
	files "github.com/ipfs/go-ipfs/commands/files"
	core "github.com/ipfs/go-ipfs/core"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
)

// Error indicating the max depth has been exceded.
var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded")

const (
	quietOptionName    = "quiet"
	silentOptionName   = "silent"
	progressOptionName = "progress"
	trickleOptionName  = "trickle"
	wrapOptionName     = "wrap-with-directory"
	hiddenOptionName   = "hidden"
	onlyHashOptionName = "only-hash"
	chunkerOptionName  = "chunker"
	pinOptionName      = "pin"
)

var AddCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Add a file or directory to ipfs.",
		ShortDescription: `
Adds contents of <path> to ipfs. Use -r to add directories (recursively).
`,
		LongDescription: `
Adds contents of <path> to ipfs. Use -r to add directories.
Note that directories are added recursively, to form the ipfs
MerkleDAG.

The wrap option, '-w', wraps the file (or files, if using the
recursive option) in a directory. This directory contains only
the files which have been added, and means that the file retains
its filename. For example:

  > ipfs add example.jpg
  added QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH example.jpg
  > ipfs add example.jpg -w
  added QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH example.jpg
  added QmaG4FuMqEBnQNn3C8XJ5bpW8kLs7zq2ZXgHptJHbKDDVx

You can now refer to the added file in a gateway, like so:

  /ipfs/QmaG4FuMqEBnQNn3C8XJ5bpW8kLs7zq2ZXgHptJHbKDDVx/example.jpg
`,
	},

	Arguments: []cmds.Argument{
		cmds.FileArg("path", true, true, "The path to a file to be added to IPFS.").EnableRecursive().EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.OptionRecursivePath, // a builtin option that allows recursive paths (-r, --recursive)
		cmds.BoolOption(quietOptionName, "q", "Write minimal output."),
		cmds.BoolOption(silentOptionName, "Write no output."),
		cmds.BoolOption(progressOptionName, "p", "Stream progress data."),
		cmds.BoolOption(trickleOptionName, "t", "Use trickle-dag format for dag generation."),
		cmds.BoolOption(onlyHashOptionName, "n", "Only chunk and hash - do not write to disk."),
		cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object."),
		cmds.BoolOption(hiddenOptionName, "H", "Include files that are hidden. Only takes effect on recursive add."),
		cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm to use."),
		cmds.BoolOption(pinOptionName, "Pin this object when adding.  Default: true."),
	},
	PreRun: func(req cmds.Request) error {
		if quiet, _, _ := req.Option(quietOptionName).Bool(); quiet {
			return nil
		}

		// ipfs cli progress bar defaults to true
		progress, found, _ := req.Option(progressOptionName).Bool()
		if !found {
			progress = true
		}

		req.SetOption(progressOptionName, progress)

		sizeFile, ok := req.Files().(files.SizeFile)
		if !ok {
			// we don't need to error, the progress bar just won't know how big the files are
			log.Warning("cannnot determine size of input file")
			return nil
		}

		sizeCh := make(chan int64, 1)
		req.Values()["size"] = sizeCh

		go func() {
			size, err := sizeFile.Size()
			if err != nil {
				log.Warningf("error getting files size: %s", err)
				// see comment above
				return
			}

			log.Debugf("Total size of file being added: %v\n", size)
			sizeCh <- size
		}()

		return nil
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		// check if repo will exceed storage limit if added
		// TODO: this doesn't handle the case if the hashed file is already in blocks (deduplicated)
		// TODO: conditional GC is disabled due to it is somehow not possible to pass the size to the daemon
		//if err := corerepo.ConditionalGC(req.Context(), n, uint64(size)); err != nil {
		//	res.SetError(err, cmds.ErrNormal)
		//	return
		//}

		progress, _, _ := req.Option(progressOptionName).Bool()
		trickle, _, _ := req.Option(trickleOptionName).Bool()
		wrap, _, _ := req.Option(wrapOptionName).Bool()
		hash, _, _ := req.Option(onlyHashOptionName).Bool()
		hidden, _, _ := req.Option(hiddenOptionName).Bool()
		silent, _, _ := req.Option(silentOptionName).Bool()
		chunker, _, _ := req.Option(chunkerOptionName).String()
		dopin, pin_found, _ := req.Option(pinOptionName).Bool()

		if !pin_found { // default
			dopin = true
		}

		if hash {
			nilnode, err := core.NewNode(n.Context(), &core.BuildCfg{
				//TODO: need this to be true or all files
				// hashed will be stored in memory!
				NilRepo: true,
			})
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
			n = nilnode
		}

		outChan := make(chan interface{}, 8)
		res.SetOutput((<-chan interface{})(outChan))

		fileAdder, err := coreunix.NewAdder(req.Context(), n, outChan)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		fileAdder.Chunker = chunker
		fileAdder.Progress = progress
		fileAdder.Hidden = hidden
		fileAdder.Trickle = trickle
		fileAdder.Wrap = wrap
		fileAdder.Pin = dopin
		fileAdder.Silent = silent

		addAllAndPin := func(f files.File) error {
			// Iterate over each top-level file and add individually. Otherwise the
			// single files.File f is treated as a directory, affecting hidden file
			// semantics.
			for {
				file, err := f.NextFile()
				if err == io.EOF {
					// Finished the list of files.
					break
				} else if err != nil {
					return err
				}
				if err := fileAdder.AddFile(file); err != nil {
					return err
				}
			}

			// copy intermediary nodes from editor to our actual dagservice
			_, err := fileAdder.Finalize()
			if err != nil {
				return err
			}

			if hash {
				return nil
			}

			return fileAdder.PinRoot()
		}

		go func() {
			defer close(outChan)
			if err := addAllAndPin(req.Files()); err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

		}()
	},
	PostRun: func(req cmds.Request, res cmds.Response) {
		if res.Error() != nil {
			return
		}
		outChan, ok := res.Output().(<-chan interface{})
		if !ok {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}
		res.SetOutput(nil)

		quiet, _, err := req.Option("quiet").Bool()
		if err != nil {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}

		progress, prgFound, err := req.Option(progressOptionName).Bool()
		if err != nil {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}

		silent, _, err := req.Option(silentOptionName).Bool()
		if err != nil {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}

		var showProgressBar bool
		if prgFound {
			showProgressBar = progress
		} else if !quiet && !silent {
			showProgressBar = true
		}

		var bar *pb.ProgressBar
		var terminalWidth int
		if showProgressBar {
			bar = pb.New64(0).SetUnits(pb.U_BYTES)
			bar.ManualUpdate = true
			bar.Start()

			// the progress bar lib doesn't give us a way to get the width of the output,
			// so as a hack we just use a callback to measure the output, then git rid of it
			terminalWidth = 0
			bar.Callback = func(line string) {
				terminalWidth = len(line)
				bar.Callback = nil
				bar.Output = res.Stderr()
				log.Infof("terminal width: %v\n", terminalWidth)
			}
			bar.Update()
		}

		var sizeChan chan int64
		s, found := req.Values()["size"]
		if found {
			sizeChan = s.(chan int64)
		}

		lastFile := ""
		var totalProgress, prevFiles, lastBytes int64

	LOOP:
		for {
			select {
			case out, ok := <-outChan:
				if !ok {
					break LOOP
				}
				output := out.(*coreunix.AddedObject)
				if len(output.Hash) > 0 {
					if showProgressBar {
						// clear progress bar line before we print "added x" output
						fmt.Fprintf(res.Stderr(), "\033[2K\r")
					}
					if quiet {
						fmt.Fprintf(res.Stdout(), "%s\n", output.Hash)
					} else {
						fmt.Fprintf(res.Stdout(), "added %s %s\n", output.Hash, output.Name)
					}

				} else {
					log.Debugf("add progress: %v %v\n", output.Name, output.Bytes)

					if !showProgressBar {
						continue
					}

					if len(lastFile) == 0 {
						lastFile = output.Name
					}
					if output.Name != lastFile || output.Bytes < lastBytes {
						prevFiles += lastBytes
						lastFile = output.Name
					}
					lastBytes = output.Bytes
					delta := prevFiles + lastBytes - totalProgress
					totalProgress = bar.Add64(delta)
				}

				if showProgressBar {
					bar.Update()
				}
			case size := <-sizeChan:
				if showProgressBar {
					bar.Total = size
					bar.ShowPercent = true
					bar.ShowBar = true
					bar.ShowTimeLeft = true
				}
			}
		}
	},
	Type: coreunix.AddedObject{},
}
<commit_msg>Add Defaults to `ipfs add`<commit_after>package commands

import (
	"fmt"
	"io"

	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
	"github.com/ipfs/go-ipfs/core/coreunix"

	cmds "github.com/ipfs/go-ipfs/commands"
	files "github.com/ipfs/go-ipfs/commands/files"
	core "github.com/ipfs/go-ipfs/core"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
)

// Error indicating the max depth has been exceded.
var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded")

const (
	quietOptionName    = "quiet"
	silentOptionName   = "silent"
	progressOptionName = "progress"
	trickleOptionName  = "trickle"
	wrapOptionName     = "wrap-with-directory"
	hiddenOptionName   = "hidden"
	onlyHashOptionName = "only-hash"
	chunkerOptionName  = "chunker"
	pinOptionName      = "pin"
)

var AddCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Add a file to ipfs.",
		ShortDescription: `
Adds contents of <path> to ipfs. Use -r to add directories.
Note that directories are added recursively, to form the ipfs
MerkleDAG.
`,
		LongDescription: `
Adds contents of <path> to ipfs. Use -r to add directories.
Note that directories are added recursively, to form the ipfs
MerkleDAG.

The wrap option, '-w', wraps the file (or files, if using the
recursive option) in a directory. This directory contains only
the files which have been added, and means that the file retains
its filename. For example:

  > ipfs add example.jpg
  added QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH example.jpg
  > ipfs add example.jpg -w
  added QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH example.jpg
  added QmaG4FuMqEBnQNn3C8XJ5bpW8kLs7zq2ZXgHptJHbKDDVx

You can now refer to the added file in a gateway, like so:

  /ipfs/QmaG4FuMqEBnQNn3C8XJ5bpW8kLs7zq2ZXgHptJHbKDDVx/example.jpg
`,
	},

	Arguments: []cmds.Argument{
		cmds.FileArg("path", true, true, "The path to a file to be added to IPFS.").EnableRecursive().EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.OptionRecursivePath, // a builtin option that allows recursive paths (-r, --recursive)
		cmds.BoolOption(quietOptionName, "q", "Write minimal output.").Default(false),
		cmds.BoolOption(silentOptionName, "Write no output.").Default(false),
		cmds.BoolOption(progressOptionName, "p", "Stream progress data.").Default(true),
		cmds.BoolOption(trickleOptionName, "t", "Use trickle-dag format for dag generation.").Default(false),
		cmds.BoolOption(onlyHashOptionName, "n", "Only chunk and hash - do not write to disk.").Default(false),
		cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object.").Default(false),
		cmds.BoolOption(hiddenOptionName, "H", "Include files that are hidden. Only takes effect on recursive add.").Default(false),
		cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm to use."),
		cmds.BoolOption(pinOptionName, "Pin this object when adding.").Default(true),
	},
	PreRun: func(req cmds.Request) error {
		if quiet, _, _ := req.Option(quietOptionName).Bool(); quiet {
			return nil
		}

		sizeFile, ok := req.Files().(files.SizeFile)
		if !ok {
			// we don't need to error, the progress bar just won't know how big the files are
			log.Warning("cannnot determine size of input file")
			return nil
		}

		sizeCh := make(chan int64, 1)
		req.Values()["size"] = sizeCh

		go func() {
			size, err := sizeFile.Size()
			if err != nil {
				log.Warningf("error getting files size: %s", err)
				// see comment above
				return
			}

			log.Debugf("Total size of file being added: %v\n", size)
			sizeCh <- size
		}()

		return nil
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		// check if repo will exceed storage limit if added
		// TODO: this doesn't handle the case if the hashed file is already in blocks (deduplicated)
		// TODO: conditional GC is disabled due to it is somehow not possible to pass the size to the daemon
		//if err := corerepo.ConditionalGC(req.Context(), n, uint64(size)); err != nil {
		//	res.SetError(err, cmds.ErrNormal)
		//	return
		//}

		progress, _, _ := req.Option(progressOptionName).Bool()
		trickle, _, _ := req.Option(trickleOptionName).Bool()
		wrap, _, _ := req.Option(wrapOptionName).Bool()
		hash, _, _ := req.Option(onlyHashOptionName).Bool()
		hidden, _, _ := req.Option(hiddenOptionName).Bool()
		silent, _, _ := req.Option(silentOptionName).Bool()
		chunker, _, _ := req.Option(chunkerOptionName).String()
		dopin, _, _ := req.Option(pinOptionName).Bool()

		if hash {
			nilnode, err := core.NewNode(n.Context(), &core.BuildCfg{
				//TODO: need this to be true or all files
				// hashed will be stored in memory!
				NilRepo: true,
			})
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
			n = nilnode
		}

		outChan := make(chan interface{}, 8)
		res.SetOutput((<-chan interface{})(outChan))

		fileAdder, err := coreunix.NewAdder(req.Context(), n, outChan)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		fileAdder.Chunker = chunker
		fileAdder.Progress = progress
		fileAdder.Hidden = hidden
		fileAdder.Trickle = trickle
		fileAdder.Wrap = wrap
		fileAdder.Pin = dopin
		fileAdder.Silent = silent

		addAllAndPin := func(f files.File) error {
			// Iterate over each top-level file and add individually. Otherwise the
			// single files.File f is treated as a directory, affecting hidden file
			// semantics.
			for {
				file, err := f.NextFile()
				if err == io.EOF {
					// Finished the list of files.
					break
				} else if err != nil {
					return err
				}
				if err := fileAdder.AddFile(file); err != nil {
					return err
				}
			}

			// copy intermediary nodes from editor to our actual dagservice
			_, err := fileAdder.Finalize()
			if err != nil {
				return err
			}

			if hash {
				return nil
			}

			return fileAdder.PinRoot()
		}

		go func() {
			defer close(outChan)
			if err := addAllAndPin(req.Files()); err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

		}()
	},
	PostRun: func(req cmds.Request, res cmds.Response) {
		if res.Error() != nil {
			return
		}
		outChan, ok := res.Output().(<-chan interface{})
		if !ok {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}
		res.SetOutput(nil)

		quiet, _, err := req.Option("quiet").Bool()
		if err != nil {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}

		progress, _, err := req.Option(progressOptionName).Bool()
		if err != nil {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}

		silent, _, err := req.Option(silentOptionName).Bool()
		if err != nil {
			res.SetError(u.ErrCast(), cmds.ErrNormal)
			return
		}

		var showProgressBar bool
		if !progress && !quiet && !silent {
			showProgressBar = true
		}

		var bar *pb.ProgressBar
		var terminalWidth int
		if showProgressBar {
			bar = pb.New64(0).SetUnits(pb.U_BYTES)
			bar.ManualUpdate = true
			bar.Start()

			// the progress bar lib doesn't give us a way to get the width of the output,
			// so as a hack we just use a callback to measure the output, then git rid of it
			terminalWidth = 0
			bar.Callback = func(line string) {
				terminalWidth = len(line)
				bar.Callback = nil
				bar.Output = res.Stderr()
				log.Infof("terminal width: %v\n", terminalWidth)
			}
			bar.Update()
		}

		var sizeChan chan int64
		s, found := req.Values()["size"]
		if found {
			sizeChan = s.(chan int64)
		}

		lastFile := ""
		var totalProgress, prevFiles, lastBytes int64

	LOOP:
		for {
			select {
			case out, ok := <-outChan:
				if !ok {
					break LOOP
				}
				output := out.(*coreunix.AddedObject)
				if len(output.Hash) > 0 {
					if showProgressBar {
						// clear progress bar line before we print "added x" output
						fmt.Fprintf(res.Stderr(), "\033[2K\r")
					}
					if quiet {
						fmt.Fprintf(res.Stdout(), "%s\n", output.Hash)
					} else {
						fmt.Fprintf(res.Stdout(), "added %s %s\n", output.Hash, output.Name)
					}

				} else {
					log.Debugf("add progress: %v %v\n", output.Name, output.Bytes)

					if !showProgressBar {
						continue
					}

					if len(lastFile) == 0 {
						lastFile = output.Name
					}
					if output.Name != lastFile || output.Bytes < lastBytes {
						prevFiles += lastBytes
						lastFile = output.Name
					}
					lastBytes = output.Bytes
					delta := prevFiles + lastBytes - totalProgress
					totalProgress = bar.Add64(delta)
				}

				if showProgressBar {
					bar.Update()
				}
			case size := <-sizeChan:
				if showProgressBar {
					bar.Total = size
					bar.ShowPercent = true
					bar.ShowBar = true
					bar.ShowTimeLeft = true
				}
			}
		}
	},
	Type: coreunix.AddedObject{},
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/GoogleCloudPlatform/terraformer/cmd"
	"log"
	"os"
	"testing"
	"time"
)

func TestTerraformerMain(t1 *testing.T) {
	t1.Run("run main", func(t1 *testing.T) {
		tCommand := cmd.NewCmdRoot()
		tCommand.SetArgs([]string{
			"import",
			"aws",
			"--regions=sa-east-1",
			//"--regions=us-west-1",
			//"--regions=us-east-1",
			//"--regions=ap-northeast-1,eu-central-1,eu-west-1,us-east-1,af-south-1,ap-northeast-2,eu-west-2,us-west-1,us-west-2,ap-east-1,ap-south-1,ap-southeast-2,ca-central-1,sa-east-1,us-east-2,ap-southeast-1,aws-global,eu-north-1,eu-south-1,eu-west-3,me-south-1",
			"--resources=\"*\"",
			//"--resources=sg",
			//"--resources=cloudformation,sg,s3",
			//"--resources=cloudformation,sg",
			//"--resources=sg,vpc",
			//"--resources=kms",
			"--profile=nubank",
			//"--retry-number=5",
			//"--retry-sleep-ms=300",
		})
		start := time.Now()
		if err := tCommand.Execute(); err != nil {
			log.Println(err)
			os.Exit(1)
		}
		log.Printf("Importing took %s", time.Since(start))

	})
}

func TestTerraformerMain2(t1 *testing.T) {
	t1.Run("run main", func(t1 *testing.T) {
		tCommand := cmd.NewCmdRoot()
		tCommand.SetArgs([]string{
			"import",
			"aws",
			//"--regions=sa-east-1",
			"--regions=us-west-1",
			//"--regions=us-east-1",
			//"--regions=ap-northeast-1,eu-central-1,eu-west-1,us-east-1,af-south-1,ap-northeast-2,eu-west-2,us-west-1,us-west-2,ap-east-1,ap-south-1,ap-southeast-2,ca-central-1,sa-east-1,us-east-2,ap-southeast-1,aws-global,eu-north-1,eu-south-1,eu-west-3,me-south-1",
			//"--resources=\"*\"",
			//"--resources=sg",
			//"--resources=cloudformation,sg,s3",
			//"--resources=cloudformation,sg",
			"--resources=sg,vpc,codepipeline,efs,ebs",
			//"--resources=kms",
			"--profile=nubank",
			//"--retry-number=5",
			//"--retry-sleep-ms=300",
		})
		start := time.Now()
		if err := tCommand.Execute(); err != nil {
			log.Println(err)
			os.Exit(1)
		}
		log.Printf("Importing took %s", time.Since(start))

	})
}
<commit_msg>deleted main_test.go<commit_after><|endoftext|>


<commit_before>package main_test

import (
	"testing"

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

func TestSomething(t *testing.T) {
	assert.False(t, false)
}
<commit_msg>update<commit_after>package main_test

import (
	"net/http"
	"testing"

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

func TestSomething(t *testing.T) {
	errs := frisby.Create("Test ping").
		Get("http://localhost:5025/ping").
		Send().
		ExpectStatus(http.StatusOK).
		Errors()

	for _, err := range errs {
		assert.Nil(t, err)
	}
}
<|endoftext|>


<commit_before>package boxrunner

import (
	"fmt"
	"github.com/christianberg/boxrunner/statemachine"
	"github.com/fsouza/go-dockerclient"
	"github.com/hashicorp/consul/api"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

type BoxRunner struct {
	Service  string
	ID       string
	port     string
	consul   *api.Client
	logger   *log.Logger
	dock     *docker.Client
	lock     *api.KVPair
	failwait time.Duration
	lastfail time.Time
}

type BoxRunnerOptions struct {
	ConsulAddress string
	ConsulClient  *api.Client
	Logger        *log.Logger
}

var DefaultOptions = &BoxRunnerOptions{
	ConsulAddress: "localhost:8500",
}

func NewBoxRunner(service_name string, options *BoxRunnerOptions) (runner *BoxRunner, err error) {
	if options == nil {
		options = DefaultOptions
	}
	completeOptions(options)

	logger := options.Logger
	dock, err := docker.NewClient("tcp://0.0.0.0:2375")
	if err != nil {
		logger.Printf("Could not initialize Docker client: %s", err)
		return
	}

	hostname, err := os.Hostname()
	if err != nil {
		logger.Printf("Could not determine hostname: %s", err)
		return
	}

	port := os.Getenv("PORT")
	runner_id := fmt.Sprintf("boxrunner-%v-%v", hostname, port)
	logger.Printf("This is boxrunner: %v", runner_id)

	runner = &BoxRunner{
		Service: service_name,
		ID:      runner_id,
		consul:  options.ConsulClient,
		logger:  options.Logger,
		dock:    dock,
		port:    port,
		lock: &api.KVPair{
			Key:   service_name,
			Value: []byte(runner_id),
		},
		failwait: 1 * time.Second,
	}
	return
}

func (b *BoxRunner) findOrCreateCheck() error {
	checks, err := b.consul.Agent().Checks()
	if err != nil {
		b.logger.Printf("Could not list existing checks: %v", err)
		return err
	}
	for _, check := range checks {
		if check.CheckID == b.ID {
			b.logger.Printf("Found existing check: %v\n", check.CheckID)
			return nil
		}
	}

	err = b.consul.Agent().CheckRegister(&api.AgentCheckRegistration{
		Name: b.ID,
		ID:   b.ID,
		AgentServiceCheck: api.AgentServiceCheck{
			HTTP:     fmt.Sprintf("http://localhost:%v/health", b.port),
			Interval: "5s",
		},
	})
	if err != nil {
		b.logger.Printf("ERROR: Could not register boxrunner healthcheck: %v", err)
	}
	return err
}

func (b *BoxRunner) findOrCreateSession() (string, error) {
	sessions, _, err := b.consul.Session().List(nil)
	if err != nil {
		b.logger.Printf("Could not list existing sessions: %v", err)
		return "", err
	}
	for _, session := range sessions {
		if session.Name == b.ID {
			b.logger.Printf("Found existing session: %v\n", session.ID)
			return session.ID, nil
		}
	}

	b.findOrCreateCheck()

	session_entry := &api.SessionEntry{
		Name:      b.ID,
		LockDelay: 5 * time.Second,
		Checks:    []string{"serfHealth", b.ID},
	}
	session_id, _, err := b.consul.Session().Create(session_entry, nil)
	if err != nil {
		b.logger.Printf("ERROR: Could not create session: %v", err)
		return "", err
	}
	b.logger.Printf("INFO: Session created (ID: %v)\n", session_id)
	return session_id, nil
}

func (b *BoxRunner) waitForLockChange(predicate func(string) bool) (err error) {
	query_options := &api.QueryOptions{
		WaitIndex: 0,
	}
	for {
		lock_status, meta, err := b.consul.KV().Get(b.lock.Key, query_options)
		if err != nil {
			b.logger.Printf("ERROR: Cannot check lock status: %s", err)
			return err
		}
		if lock_status == nil || predicate(lock_status.Session) {
			b.logger.Println("INFO: Lock was released")
			return nil
		}
		query_options.WaitIndex = meta.LastIndex
	}
}

func (b *BoxRunner) Run() (success bool, error error) {
	http.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) {
		io.WriteString(w, "OK")
	})

	go http.ListenAndServe("127.0.0.1:"+b.port, nil)

	machine := &statemachine.Machine{
		Handlers: map[string]statemachine.Handler{},
		Logger:   b.logger,
	}

	machine.AddState("INIT", func() string {
		return "DISCOVER"
	})

	machine.AddState("DISCOVER", func() string {
		session_id, err := b.findOrCreateSession()
		if err != nil {
			return "FAILED"
		}
		b.lock.Session = session_id

		if err := b.dock.Ping(); err != nil {
			b.logger.Printf("ERROR: Cannot ping docker server: %v", err)
			return "FAILED"
		}
		return "COMPETE"
	})

	machine.AddState("COMPETE", func() string {
		b.logger.Printf("INFO: Trying to acquire lock for %s\n", b.Service)
		success, _, err := b.consul.KV().Acquire(b.lock, nil)
		if err != nil {
			b.logger.Printf("ERROR: Could not acquire lock: %s", err)
			return "FAILED"
		}
		if success {
			b.logger.Println("INFO: Lock acquired!")
			return "START"
		} else {
			lock_status, _, _ := b.consul.KV().Get(b.lock.Key, nil)
			if lock_status != nil {
				b.logger.Printf("INFO: Lock is already taken by: %s", lock_status.Value)
			}
			return "SLEEP"
		}
	})

	machine.AddState("START", func() string {
		if err := b.dock.PullImage(
			docker.PullImageOptions{
				Repository: "busybox",
			},
			docker.AuthConfiguration{},
		); err != nil {
			b.logger.Printf("ERROR: Failed to pull image: %v", err)
			return "FAILED"
		}
		b.logger.Println("INFO: Image pulled")

		container, err := b.dock.CreateContainer(
			docker.CreateContainerOptions{
				Name: b.ID,
				Config: &docker.Config{
					Image: "busybox",
					Cmd:   []string{"/bin/sleep", "10"},
				},
			},
		)
		if err != nil {
			b.logger.Printf("ERROR: Failed to create docker container: %v", err)
			return "FAILED"
		}
		b.logger.Printf("INFO: Container created: %v", container.ID)

		if err := b.dock.StartContainer(container.ID, &docker.HostConfig{}); err != nil {
			b.logger.Printf("ERROR: Failed to start docker container: %v", err)
			return "FAILED"
		}
		b.logger.Printf("INFO: Container started: %v", container.ID)
		return "RUNNING"
	})

	machine.AddState("RUNNING", func() string {
		lock_watch := make(chan string)
		cancel_lock_watch := make(chan bool, 1)
		go func() {
			err := b.waitForLockChange(func(s string) bool { return s != b.lock.Session })
			select {
			case <-cancel_lock_watch:
				return
			default:
				if err != nil {
					lock_watch <- "FAILED"
					return
				}
				lock_watch <- "STOP"
				return
			}
		}()

		container_watch := make(chan string)
		cancel_container_watch := make(chan bool, 1)
		go func() {
			exit_code, err := b.dock.WaitContainer(b.ID)
			select {
			case <-cancel_container_watch:
				return
			default:
				if err != nil {
					b.logger.Printf("ERROR: Waiting for Docker container exit failed: %v", err)
					container_watch <- "FAILED"
					return
				}
				level := "INFO"
				if exit_code > 0 {
					level = "WARN"
				}
				b.logger.Printf("%v: Docker container exited with code %v\n", level, exit_code)
				container_watch <- "RELEASE"
				return
			}
		}()

		select {
		case s := <-lock_watch:
			cancel_container_watch <- true
			return s
		case s := <-container_watch:
			cancel_lock_watch <- true
			return s
		}
	})

	machine.AddState("SLEEP", func() string {
		time.Sleep(3 * time.Second)
		err := b.waitForLockChange(func(s string) bool { return s == "" })
		if err != nil {
			return "FAILED"
		}
		return "COMPETE"
	})

	machine.AddState("STOP", func() string {
		return "RELEASE"
	})

	machine.AddState("RELEASE", func() string {
		b.logger.Printf("INFO: Releasing lock for %s\n", b.Service)
		_, _, err := b.consul.KV().Release(b.lock, nil)
		if err != nil {
			b.logger.Printf("ERROR: Could not release lock: %v", err)
			return "FAILED"
		}
		return "REMOVE"
	})

	machine.AddState("REMOVE", func() string {
		err := b.dock.RemoveContainer(docker.RemoveContainerOptions{ID: b.ID})
		if err != nil {
			b.logger.Printf("ERROR: Could not remove container: %v", err)
			return "FAILED"
		}
		return "COMPETE"
	})

	machine.AddState("FAILED", func() string {
		if b.lastfail.Add(2 * b.failwait).Before(time.Now()) {
			b.failwait = 1 * time.Second
		}
		b.lastfail = time.Now()
		b.logger.Printf("WARN: Waiting for %v", b.failwait)
		time.Sleep(b.failwait)
		b.failwait = 2 * b.failwait
		if b.failwait > 5*time.Minute {
			b.failwait = 5 * time.Minute
		}
		return "DISCOVER"
	})

	return machine.Run()
}

func completeOptions(options *BoxRunnerOptions) {
	if options.ConsulClient == nil {
		if options.ConsulAddress == "" {
			options.ConsulAddress = DefaultOptions.ConsulAddress
		}
		var err error
		options.ConsulClient, err = api.NewClient(api.DefaultConfig())
		if err != nil {
			panic("Failed to create consul-api Client")
		}
	}

	if options.Logger == nil {
		options.Logger = log.New(os.Stdout, "", 0)
	}
}
<commit_msg>Use Docker TLS client<commit_after>package boxrunner

import (
	"fmt"
	"github.com/christianberg/boxrunner/statemachine"
	"github.com/fsouza/go-dockerclient"
	"github.com/hashicorp/consul/api"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

type BoxRunner struct {
	Service  string
	ID       string
	port     string
	consul   *api.Client
	logger   *log.Logger
	dock     *docker.Client
	lock     *api.KVPair
	failwait time.Duration
	lastfail time.Time
}

type BoxRunnerOptions struct {
	ConsulAddress string
	ConsulClient  *api.Client
	Logger        *log.Logger
}

var DefaultOptions = &BoxRunnerOptions{
	ConsulAddress: "localhost:8500",
}

func createDockerClient() (*docker.Client, error) {
	endpoint := os.Getenv("DOCKER_HOST")
	path := os.Getenv("DOCKER_CERT_PATH")
	ca := fmt.Sprintf("%s/ca.pem", path)
	cert := fmt.Sprintf("%s/cert.pem", path)
	key := fmt.Sprintf("%s/key.pem", path)
	client, err := docker.NewTLSClient(endpoint, cert, key, ca)
	return client, err
}

func NewBoxRunner(service_name string, options *BoxRunnerOptions) (runner *BoxRunner, err error) {
	if options == nil {
		options = DefaultOptions
	}
	completeOptions(options)

	logger := options.Logger
	dock, err := createDockerClient()
	if err != nil {
		logger.Printf("Could not initialize Docker client: %s", err)
		return
	}

	hostname, err := os.Hostname()
	if err != nil {
		logger.Printf("Could not determine hostname: %s", err)
		return
	}

	port := os.Getenv("PORT")
	runner_id := fmt.Sprintf("boxrunner-%v-%v", hostname, port)
	logger.Printf("This is boxrunner: %v", runner_id)

	runner = &BoxRunner{
		Service: service_name,
		ID:      runner_id,
		consul:  options.ConsulClient,
		logger:  options.Logger,
		dock:    dock,
		port:    port,
		lock: &api.KVPair{
			Key:   service_name,
			Value: []byte(runner_id),
		},
		failwait: 1 * time.Second,
	}
	return
}

func (b *BoxRunner) findOrCreateCheck() error {
	checks, err := b.consul.Agent().Checks()
	if err != nil {
		b.logger.Printf("Could not list existing checks: %v", err)
		return err
	}
	for _, check := range checks {
		if check.CheckID == b.ID {
			b.logger.Printf("Found existing check: %v\n", check.CheckID)
			return nil
		}
	}

	err = b.consul.Agent().CheckRegister(&api.AgentCheckRegistration{
		Name: b.ID,
		ID:   b.ID,
		AgentServiceCheck: api.AgentServiceCheck{
			HTTP:     fmt.Sprintf("http://localhost:%v/health", b.port),
			Interval: "5s",
		},
	})
	if err != nil {
		b.logger.Printf("ERROR: Could not register boxrunner healthcheck: %v", err)
	}
	return err
}

func (b *BoxRunner) findOrCreateSession() (string, error) {
	sessions, _, err := b.consul.Session().List(nil)
	if err != nil {
		b.logger.Printf("Could not list existing sessions: %v", err)
		return "", err
	}
	for _, session := range sessions {
		if session.Name == b.ID {
			b.logger.Printf("Found existing session: %v\n", session.ID)
			return session.ID, nil
		}
	}

	b.findOrCreateCheck()

	session_entry := &api.SessionEntry{
		Name:      b.ID,
		LockDelay: 5 * time.Second,
		Checks:    []string{"serfHealth", b.ID},
	}
	session_id, _, err := b.consul.Session().Create(session_entry, nil)
	if err != nil {
		b.logger.Printf("ERROR: Could not create session: %v", err)
		return "", err
	}
	b.logger.Printf("INFO: Session created (ID: %v)\n", session_id)
	return session_id, nil
}

func (b *BoxRunner) waitForLockChange(predicate func(string) bool) (err error) {
	query_options := &api.QueryOptions{
		WaitIndex: 0,
	}
	for {
		lock_status, meta, err := b.consul.KV().Get(b.lock.Key, query_options)
		if err != nil {
			b.logger.Printf("ERROR: Cannot check lock status: %s", err)
			return err
		}
		if lock_status == nil || predicate(lock_status.Session) {
			b.logger.Println("INFO: Lock was released")
			return nil
		}
		query_options.WaitIndex = meta.LastIndex
	}
}

func (b *BoxRunner) Run() (success bool, error error) {
	http.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) {
		io.WriteString(w, "OK")
	})

	go http.ListenAndServe("127.0.0.1:"+b.port, nil)

	machine := &statemachine.Machine{
		Handlers: map[string]statemachine.Handler{},
		Logger:   b.logger,
	}

	machine.AddState("INIT", func() string {
		return "DISCOVER"
	})

	machine.AddState("DISCOVER", func() string {
		session_id, err := b.findOrCreateSession()
		if err != nil {
			return "FAILED"
		}
		b.lock.Session = session_id

		if err := b.dock.Ping(); err != nil {
			b.logger.Printf("ERROR: Cannot ping docker server: %v", err)
			return "FAILED"
		}
		return "COMPETE"
	})

	machine.AddState("COMPETE", func() string {
		b.logger.Printf("INFO: Trying to acquire lock for %s\n", b.Service)
		success, _, err := b.consul.KV().Acquire(b.lock, nil)
		if err != nil {
			b.logger.Printf("ERROR: Could not acquire lock: %s", err)
			return "FAILED"
		}
		if success {
			b.logger.Println("INFO: Lock acquired!")
			return "START"
		} else {
			lock_status, _, _ := b.consul.KV().Get(b.lock.Key, nil)
			if lock_status != nil {
				b.logger.Printf("INFO: Lock is already taken by: %s", lock_status.Value)
			}
			return "SLEEP"
		}
	})

	machine.AddState("START", func() string {
		if err := b.dock.PullImage(
			docker.PullImageOptions{
				Repository: "busybox",
			},
			docker.AuthConfiguration{},
		); err != nil {
			b.logger.Printf("ERROR: Failed to pull image: %v", err)
			return "FAILED"
		}
		b.logger.Println("INFO: Image pulled")

		container, err := b.dock.CreateContainer(
			docker.CreateContainerOptions{
				Name: b.ID,
				Config: &docker.Config{
					Image: "busybox",
					Cmd:   []string{"/bin/sleep", "10"},
				},
			},
		)
		if err != nil {
			b.logger.Printf("ERROR: Failed to create docker container: %v", err)
			return "FAILED"
		}
		b.logger.Printf("INFO: Container created: %v", container.ID)

		if err := b.dock.StartContainer(container.ID, &docker.HostConfig{}); err != nil {
			b.logger.Printf("ERROR: Failed to start docker container: %v", err)
			return "FAILED"
		}
		b.logger.Printf("INFO: Container started: %v", container.ID)
		return "RUNNING"
	})

	machine.AddState("RUNNING", func() string {
		lock_watch := make(chan string)
		cancel_lock_watch := make(chan bool, 1)
		go func() {
			err := b.waitForLockChange(func(s string) bool { return s != b.lock.Session })
			select {
			case <-cancel_lock_watch:
				return
			default:
				if err != nil {
					lock_watch <- "FAILED"
					return
				}
				lock_watch <- "STOP"
				return
			}
		}()

		container_watch := make(chan string)
		cancel_container_watch := make(chan bool, 1)
		go func() {
			exit_code, err := b.dock.WaitContainer(b.ID)
			select {
			case <-cancel_container_watch:
				return
			default:
				if err != nil {
					b.logger.Printf("ERROR: Waiting for Docker container exit failed: %v", err)
					container_watch <- "FAILED"
					return
				}
				level := "INFO"
				if exit_code > 0 {
					level = "WARN"
				}
				b.logger.Printf("%v: Docker container exited with code %v\n", level, exit_code)
				container_watch <- "RELEASE"
				return
			}
		}()

		select {
		case s := <-lock_watch:
			cancel_container_watch <- true
			return s
		case s := <-container_watch:
			cancel_lock_watch <- true
			return s
		}
	})

	machine.AddState("SLEEP", func() string {
		time.Sleep(3 * time.Second)
		err := b.waitForLockChange(func(s string) bool { return s == "" })
		if err != nil {
			return "FAILED"
		}
		return "COMPETE"
	})

	machine.AddState("STOP", func() string {
		return "RELEASE"
	})

	machine.AddState("RELEASE", func() string {
		b.logger.Printf("INFO: Releasing lock for %s\n", b.Service)
		_, _, err := b.consul.KV().Release(b.lock, nil)
		if err != nil {
			b.logger.Printf("ERROR: Could not release lock: %v", err)
			return "FAILED"
		}
		return "REMOVE"
	})

	machine.AddState("REMOVE", func() string {
		err := b.dock.RemoveContainer(docker.RemoveContainerOptions{ID: b.ID})
		if err != nil {
			b.logger.Printf("ERROR: Could not remove container: %v", err)
			return "FAILED"
		}
		return "COMPETE"
	})

	machine.AddState("FAILED", func() string {
		if b.lastfail.Add(2 * b.failwait).Before(time.Now()) {
			b.failwait = 1 * time.Second
		}
		b.lastfail = time.Now()
		b.logger.Printf("WARN: Waiting for %v", b.failwait)
		time.Sleep(b.failwait)
		b.failwait = 2 * b.failwait
		if b.failwait > 5*time.Minute {
			b.failwait = 5 * time.Minute
		}
		return "DISCOVER"
	})

	return machine.Run()
}

func completeOptions(options *BoxRunnerOptions) {
	if options.ConsulClient == nil {
		if options.ConsulAddress == "" {
			options.ConsulAddress = DefaultOptions.ConsulAddress
		}
		var err error
		options.ConsulClient, err = api.NewClient(api.DefaultConfig())
		if err != nil {
			panic("Failed to create consul-api Client")
		}
	}

	if options.Logger == nil {
		options.Logger = log.New(os.Stdout, "", 0)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Brian J. Downs
//
// 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 openweathermap

import (
	"fmt"
	"net/http"
	"net/url"
)

// Slice of type string of the valid parameters to be sent from a station.
// The API refers to this data as the "Weather station data transmission protocol"
var StationDataParameters = []string{
	"wind_dir",   // Wind direction
	"wind_speed", // Wind speed
	"wind_gust",  // Wind gust speed
	"temp",       // Temperature
	"humidity",   // Relative humidty
	"pressure",   // Atmospheric pressure
	"rain_1h",    // Rain in the last hour
	"rain_24h",   // Rain in the last 24 hours
	"rain_today", // Rain since midnight
	"snow",       // Snow in the last 24 hours
	"lum",        // Brightness
	"lat",        // Latitude
	"long",       // Longitude
	"alt",        // Altitude
	"radiation",  // Radiation
	"dewpoint",   // Dew point
	"uv",         // UV index
	"name",       // Weather station name
}

// ValidateStationDataParameter will make sure that whatever parameter
// supplied is one that can actually be used in the POST request.
func ValidateStationDataParameter(param string) bool {
	for _, p := range StationDataParameters {
		if param == p {
			return true
		}
	}
	return false
}

// ConvertToURLValues will convert a map to a url.Values instance. We're
// taking a map[string]string instead of something more type specific since
// the url.Values instance only takes strings to create the URL values.
func ConvertToURLValues(data map[string]string) string {
	v := url.Values{}
	for key, val := range data {
		v.Set(key, val)
	}
	return v.Encode()
}

// SendStationData will send an instance the provided url.Values to the
// provided URL.
func SendStationData(data url.Values) {
	resp, err := http.PostForm(dataPostURL, data)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(resp.Body)
}
<commit_msg>station formatting<commit_after>// Copyright 2015 Brian J. Downs
//
// 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 openweathermap

import (
	"fmt"
	"net/http"
	"net/url"
)

// Slice of type string of the valid parameters to be sent from a station.
// The API refers to this data as the "Weather station data transmission protocol"
var StationDataParameters = []string{
	"wind_dir",   // Wind direction
	"wind_speed", // Wind speed
	"wind_gust",  // Wind gust speed
	"temp",       // Temperature
	"humidity",   // Relative humidty
	"pressure",   // Atmospheric pressure
	"rain_1h",    // Rain in the last hour
	"rain_24h",   // Rain in the last 24 hours
	"rain_today", // Rain since midnight
	"snow",       // Snow in the last 24 hours
	"lum",        // Brightness
	"lat",        // Latitude
	"long",       // Longitude
	"alt",        // Altitude
	"radiation",  // Radiation
	"dewpoint",   // Dew point
	"uv",         // UV index
	"name",       // Weather station name
}

// ValidateStationDataParameter will make sure that whatever parameter
// supplied is one that can actually be used in the POST request.
func ValidateStationDataParameter(param string) bool {
	for _, p := range StationDataParameters {
		if param == p {
			return true
		}
	}

	return false
}

// ConvertToURLValues will convert a map to a url.Values instance. We're
// taking a map[string]string instead of something more type specific since
// the url.Values instance only takes strings to create the URL values.
func ConvertToURLValues(data map[string]string) string {
	v := url.Values{}

	for key, val := range data {
		v.Set(key, val)
	}

	return v.Encode()
}

// SendStationData will send an instance the provided url.Values to the
// provided URL.
func SendStationData(data url.Values) {
	resp, err := http.PostForm(dataPostURL, data)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(resp.Body)
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func Test_is_url(t *testing.T) {
	url1 := "https://github.com/ekalinin/envirius/blob/master/README.md"
	if !IsUrl(url1) {
		t.Error("This is url: ", url1)
	}

	url2 := "./README.md"
	if IsUrl(url2) {
		t.Error("This is not url: ", url2)
	}
}

func Test_grab_toc_onerow(t *testing.T) {
	t.Skip("not ready yet")
	toc_expected := []string{
		"  * [README in another language](#readme-in-another-language)",
	}
	toc := GrabToc(`

README in another language

	`)
	if toc[0] != toc_expected[0] {
		t.Error("Res :", toc, "\nExpected     :", toc_expected)
	}
}

func Test_grab_toc_onerow_with_newlines(t *testing.T) {
	t.Skip("not ready yet")
	toc_expected := []string{
		"  * [README in another language](#readme-in-another-language)",
	}
	toc := GrabToc(`

		README in another language

	`)
	if toc[0] != toc_expected[0] {
		t.Error("Res :", toc, "\nExpected     :", toc_expected)
	}
}

func Test_grab_toc_multiline_origin_github(t *testing.T) {

	toc_expected := []string{
		"  * [How to add a plugin?](#how-to-add-a-plugin)",
		"    * [Mandatory elements](#mandatory-elements)",
		"      * [plug_list_versions](#plug_list_versions)",
	}
	toc := GrabToc(`

How to add a plugin?

All plugins are in the directory
nv-plugins.
If you need to add support for a new language you should add it as plugin
inside this directory.

Mandatory elements

If you create a plugin which builds all stuff from source then In a simplest
case you need to implement 2 functions in the plugin's body:

plug_list_versions

This function should return list of available versions of the plugin.
For example:

	`)
	for i := 0; i <= len(toc_expected)-1; i++ {
		if toc[i] != toc_expected[i] {
			t.Error("Res :", toc[i], "\nExpected     :", toc_expected[i])
		}
	}
}

func Test_GrabToc_backquoted(t *testing.T) {
	toc_expected := []string{
		"  * [The command foo1](#the-command-foo1)",
		"  * [The command foo2 is better](#the-command-foo2-is-better)",
		"  * [The command bar1](#the-command-bar1)",
		"  * [The command bar2 is better](#the-command-bar2-is-better)",
	}

	toc := GrabToc(`

The command foo1

Blabla...

The command foo2 is better

Blabla...

The command bar1

Blabla...

The command bar2 is better

Blabla...

	`)

	for i := 0; i <= len(toc_expected)-1; i++ {
		if toc[i] != toc_expected[i] {
			t.Error("Res :", toc[i], "\nExpected      :", toc_expected[i])
		}
	}
}
<commit_msg>improved test Test_GrabToc_backquoted<commit_after>package main

import (
	"testing"
)

func Test_is_url(t *testing.T) {
	url1 := "https://github.com/ekalinin/envirius/blob/master/README.md"
	if !IsUrl(url1) {
		t.Error("This is url: ", url1)
	}

	url2 := "./README.md"
	if IsUrl(url2) {
		t.Error("This is not url: ", url2)
	}
}

func Test_grab_toc_onerow(t *testing.T) {
	t.Skip("not ready yet")
	toc_expected := []string{
		"  * [README in another language](#readme-in-another-language)",
	}
	toc := GrabToc(`

README in another language

	`)
	if toc[0] != toc_expected[0] {
		t.Error("Res :", toc, "\nExpected     :", toc_expected)
	}
}

func Test_grab_toc_onerow_with_newlines(t *testing.T) {
	t.Skip("not ready yet")
	toc_expected := []string{
		"  * [README in another language](#readme-in-another-language)",
	}
	toc := GrabToc(`

		README in another language

	`)
	if toc[0] != toc_expected[0] {
		t.Error("Res :", toc, "\nExpected     :", toc_expected)
	}
}

func Test_grab_toc_multiline_origin_github(t *testing.T) {

	toc_expected := []string{
		"  * [How to add a plugin?](#how-to-add-a-plugin)",
		"    * [Mandatory elements](#mandatory-elements)",
		"      * [plug_list_versions](#plug_list_versions)",
	}
	toc := GrabToc(`

How to add a plugin?

All plugins are in the directory
nv-plugins.
If you need to add support for a new language you should add it as plugin
inside this directory.

Mandatory elements

If you create a plugin which builds all stuff from source then In a simplest
case you need to implement 2 functions in the plugin's body:

plug_list_versions

This function should return list of available versions of the plugin.
For example:

	`)
	for i := 0; i <= len(toc_expected)-1; i++ {
		if toc[i] != toc_expected[i] {
			t.Error("Res :", toc[i], "\nExpected     :", toc_expected[i])
		}
	}
}

func Test_GrabToc_backquoted(t *testing.T) {
	toc_expected := []string{
		"  * [The command foo1](#the-command-foo1)",
		"    * [The command foo2 is better](#the-command-foo2-is-better)",
		"  * [The command bar1](#the-command-bar1)",
		"    * [The command bar2 is better](#the-command-bar2-is-better)",
	}

	toc := GrabToc(`

The command foo1

Blabla...

The command foo2 is better

Blabla...

The command bar1

Blabla...

The command bar2 is better

Blabla...

	`)

	for i := 0; i <= len(toc_expected)-1; i++ {
		if toc[i] != toc_expected[i] {
			t.Error("Res :", toc[i], "\nExpected      :", toc_expected[i])
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"

	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/spf13/viper"
)

var serv *httpServer

func TestMain(m *testing.M) {

	if err := i18n.ParseTranslationFileBytes("lang/en-us.all.json", readFile("lang/en-us.all.json", true)); err != nil {
		logFatal(err, funcName(), "Error read language file check in config.yaml if dev=false", "")
	}

	translateFunc, err := i18n.Tfunc("en-us")
	checkErr(err, funcName(), "Problem with loading translate file", "")

	channels := make(map[string]chan bloomsky.Bloomsky)
	serv, err = createWebServer(channels["web"], ":1111", ":2222", translateFunc, true)
	os.Exit(m.Run())
}

func TestSomething(t *testing.T) {
	viper.SetConfigName("config")
	viper.AddConfigPath(".")
	if err := viper.ReadInConfig(); err != nil {
		fmt.Printf("%v", err)
	}
}

/*
func TestMain(m *testing.M) {
	i18n.MustLoadTranslationFile("lang/en-US.all.json")
	i18n.MustLoadTranslationFile("lang/fr.all.json")
	mylog.Init(mylog.ERROR)

	os.Exit(m.Run())
}

func TestReadConfigFound(t *testing.T) {
	if err := readConfig("configForTest"); err != nil {
		fmt.Printf("%v", err)
	}
}*/

/*func TestReadConfigNotFound(t *testing.T) {
	if err := readConfig("configError"); err != nil {
		fmt.Printf("%v", err)
	}
}*/

func TestHanlder(t *testing.T) {
	req, err := http.NewRequest(
		http.MethodGet,
		"http://localhost:1111",
		nil,
	)
	if err != nil {
		t.Fatalf("Could not request: %v", err)
	}

	if err := i18n.ParseTranslationFileBytes("lang/en-us.all.json", readFile("lang/en-us.all.json", true)); err != nil {
		logFatal(err, funcName(), "Error read language file check in config.yaml if dev=false", "")
	}

	translateFunc, err := i18n.Tfunc("en-us")
	checkErr(err, funcName(), "Problem with loading translate file", "")

	channels := make(map[string]chan bloomsky.Bloomsky)

	rec := httptest.NewRecorder()

	httpServ, err := createWebServer(channels["web"], ":1112", ":2223", translateFunc, true)
	httpServ.home(rec, req)

	if rec.Code != http.StatusOK {
		t.Errorf("expected 200; got %d", rec.Code)
	}
	if !strings.Contains(rec.Body.String(), "Est") {
		t.Errorf("unexpected body; %q", rec.Body.String())
	}
}

func BenchmarkHanlder(b *testing.B) {

	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest(
			http.MethodGet,
			"http://localhost:1111",
			nil,
		)
		if err != nil {
			logFatal(err, funcName(), "Could not request: %v", "")
		}

		rec := httptest.NewRecorder()
		serv.home(rec, req)

		if rec.Code != http.StatusOK {
			b.Errorf("expected 200; got %d", rec.Code)
		}
		if !strings.Contains(rec.Body.String(), "Est") {
			b.Errorf("unexpected body; %q", rec.Body.String())
		}
	}
}
<commit_msg>improve unit tes<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"

	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/spf13/viper"
)

var serv *httpServer

func TestMain(m *testing.M) {

	if err := i18n.ParseTranslationFileBytes("lang/en-us.all.json", readFile("lang/en-us.all.json", true)); err != nil {
		logFatal(err, funcName(), "Error read language file check in config.yaml if dev=false", "")
	}

	translateFunc, err := i18n.Tfunc("en-us")
	checkErr(err, funcName(), "Problem with loading translate file", "")

	channels := make(map[string]chan bloomsky.Bloomsky)
	serv, err = createWebServer(channels["web"], ":1110", ":2220", translateFunc, true)
	os.Exit(m.Run())
}

func TestConfig(t *testing.T) {
	viper.SetConfigName("config")
	viper.AddConfigPath(".")
	if err := viper.ReadInConfig(); err != nil {
		fmt.Printf("%v", err)
	}
}

func TestHanlderHome(t *testing.T) {
	req, err := http.NewRequest(
		http.MethodGet,
		"http://localhost:1110/",
		nil,
	)
	if err != nil {
		logFatal(err, funcName(), "Could not request: %v", "")
	}

	rec := httptest.NewRecorder()
	serv.home(rec, req)

	if rec.Code != http.StatusOK {
		t.Errorf("expected 200; got %d", rec.Code)
	}
	if !strings.Contains(rec.Body.String(), "est") {
		t.Errorf("unexpected body; %q", rec.Body.String())
	}
}

func TestHanlderLog(t *testing.T) {
	req, err := http.NewRequest(
		http.MethodGet,
		"http://localhost:1110/log",
		nil,
	)
	if err != nil {
		logFatal(err, funcName(), "Could not request: %v", "")
	}

	rec := httptest.NewRecorder()
	serv.log(rec, req)

	if rec.Code != http.StatusOK {
		t.Errorf("expected 200; got %d", rec.Code)
	}
	if !strings.Contains(rec.Body.String(), "Server HTTPS listen on port") {
		t.Errorf("unexpected body; %q", rec.Body.String())
	}
}

func TestHanlderHistory(t *testing.T) {
	req, err := http.NewRequest(
		http.MethodGet,
		"http://localhost:1110/history",
		nil,
	)
	if err != nil {
		logFatal(err, funcName(), "Could not request: %v", "")
	}

	rec := httptest.NewRecorder()
	serv.history(rec, req)

	if rec.Code != http.StatusOK {
		t.Errorf("expected 200; got %d", rec.Code)
	}
	if !strings.Contains(rec.Body.String(), "corechart") {
		t.Errorf("unexpected body; %q", rec.Body.String())
	}
}

func BenchmarkHanlder(b *testing.B) {

	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest(
			http.MethodGet,
			"http://localhost:1110",
			nil,
		)
		if err != nil {
			logFatal(err, funcName(), "Could not request: %v", "")
		}

		rec := httptest.NewRecorder()
		serv.home(rec, req)

		if rec.Code != http.StatusOK {
			b.Errorf("expected 200; got %d", rec.Code)
		}
		if !strings.Contains(rec.Body.String(), "Est") {
			b.Errorf("unexpected body; %q", rec.Body.String())
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"testing"
)

// TODO: Write required setup for a local resolver with a variety of domains and test cases
func TestCheckWildcard(t *testing.T) {
	// A simple test domain -- please don't abuse it
	ips := checkWildcard("glugger.ss23.geek.nz")
	expected := []string{"127.0.0.23"}
	if reflect.DeepEqual(ips, expected) == false {
		t.Error(
			"Wildcard detection returned an unexpected result.",
			"Expected: ", expected,
			" - Got: ", ips,
		)
	}

}
<commit_msg>Add a test for zonetransfer<commit_after>package main

import (
	"reflect"
	"testing"
)

// TODO: Write required setup for a local resolver with a variety of domains and test cases
func TestCheckWildcard(t *testing.T) {
	// A simple test domain -- please don't abuse it
	ips := checkWildcard("glugger.ss23.geek.nz")
	expected := []string{"127.0.0.23"}
	if reflect.DeepEqual(ips, expected) == false {
		t.Error(
			"Wildcard detection returned an unexpected result.",
			"Expected: ", expected,
			" - Got: ", ips,
		)
	}

}

func TestZoneTransfer(t *testing.T) {
	// As before, this should be testing against a domain we control, but this will work for now
	if checkZoneTransfer("zonetransfer.me") == false {
		t.Error("Zone transfer attempt on zonetransfer.me failed")
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"testing"
	"time"

	"github.com/apcera/nats"
	"github.com/cloudfoundry-incubator/metricz/collector_registrar"
	"github.com/cloudfoundry/gunk/natsrunner"
	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

func TestRuntimeMetricsServer(t *testing.T) {
	RegisterFailHandler(Fail)
	SetDefaultConsistentlyDuration(time.Second)
	SetDefaultConsistentlyPollingInterval(100 * time.Millisecond)
	SetDefaultEventuallyPollingInterval(100 * time.Millisecond)
	RunSpecs(t, "RuntimeMetricsServer Suite")
}

func NewMetricServer(binPath string, metricsPort, etcdPort, natsPort int) *ginkgomon.Runner {
	return ginkgomon.New(ginkgomon.Config{
		Name:          "metrics",
		AnsiColorCode: "61m",
		Command: exec.Command(
			binPath,
			"-port", fmt.Sprintf("%d", metricsPort),
			"-etcdCluster", fmt.Sprintf("http://127.0.0.1:%d", etcdPort),
			"-natsAddresses", fmt.Sprintf("127.0.0.1:%d", natsPort),
			"-index", "5",
			"-username", "the-username",
			"-password", "the-password",
		),
	})
}

var _ = Describe("Main", func() {
	var metricsPort int
	var natsPort int
	var etcdPort int
	var natsRunner *natsrunner.NATSRunner
	var etcdRunner *etcdstorerunner.ETCDClusterRunner
	var metricsServerPath string
	var metricsServer *ginkgomon.Runner
	var metricsProcess ifrit.Process

	BeforeSuite(func() {
		var err error
		metricsServerPath, err = gexec.Build("github.com/cloudfoundry-incubator/runtime-metrics-server")
		Ω(err).ShouldNot(HaveOccurred())
		metricsPort = 5678 + GinkgoParallelNode()
		etcdPort = 5001 + GinkgoParallelNode()
		natsPort = 4228 + GinkgoParallelNode()
	})

	AfterSuite(func() {
		gexec.CleanupBuildArtifacts()
	})

	BeforeEach(func() {
		etcdRunner = etcdstorerunner.NewETCDClusterRunner(etcdPort, 1)
		natsRunner = natsrunner.NewNATSRunner(natsPort)
		metricsServer = NewMetricServer(metricsServerPath, metricsPort, etcdPort, natsPort)
	})

	JustBeforeEach(func() {
		metricsProcess = ifrit.Envoke(metricsServer)
	})

	AfterEach(func() {
		defer func() {
			metricsProcess.Signal(os.Kill)
			Eventually(metricsProcess.Wait(), time.Second).Should(Receive())
		}()

		metricsProcess.Signal(os.Interrupt)
		Eventually(metricsProcess.Wait(), 5*time.Second).Should(Receive(BeNil()))
	})

	Context("When nats is avaialble", func() {
		BeforeEach(func() {
			natsRunner.Start()
			etcdRunner.Start()
		})

		AfterEach(func() {
			natsRunner.Stop()
			etcdRunner.Stop()
		})

		It("reports started", func() {
			Eventually(metricsServer).Should(gbytes.Say("started"))
		})

		Context("and we are subsribed to component announcements", func() {
			var reg collector_registrar.AnnounceComponentMessage
			var announcements <-chan bool

			BeforeEach(func() {
				receivedAnnouncements := make(chan bool)

				announcements = receivedAnnouncements

				natsRunner.MessageBus.Subscribe("vcap.component.announce", func(message *nats.Msg) {
					err := json.Unmarshal(message.Data, &reg)
					Ω(err).ShouldNot(HaveOccurred())

					receivedAnnouncements <- true
				})
			})

			JustBeforeEach(func() {
				Eventually(announcements).Should(Receive())
			})

			It("reports the correct index", func() {
				Ω(reg.Index).Should(Equal(uint(5)))
			})

			It("listens on /varz of the reported host", func() {
				Eventually(func() error {
					conn, err := net.Dial("tcp", reg.Host)
					if err != nil {
						return nil
					}
					defer conn.Close()
					return err
				}).ShouldNot(HaveOccurred())

				req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/varz", reg.Host), nil)
				Ω(err).ShouldNot(HaveOccurred())
				req.SetBasicAuth("the-username", "the-password")

				resp, err := http.DefaultClient.Do(req)
				Ω(err).ShouldNot(HaveOccurred())
				Ω(resp.StatusCode).Should(Equal(200))
			})
		})
	})

	Context("When nats is not avaiable", func() {
		BeforeEach(func() {
			etcdRunner.Start()
		})

		AfterEach(func() {
			etcdRunner.Stop()
		})

		It("should not start server", func() {
			Consistently(metricsServer).ShouldNot(gbytes.Say("started"))
		})

		It("does not exit", func() {
			Consistently(metricsProcess.Wait()).ShouldNot(Receive())
		})
	})

	Context("When nats not available at first, but eventually becomes available", func() {
		BeforeEach(func() {
			etcdRunner.Start()
		})

		AfterEach(func() {
			natsRunner.Stop()
			etcdRunner.Stop()
		})

		It("should not start server until nats becomes avaialble", func() {
			Consistently(metricsServer).ShouldNot(gbytes.Say("started"))

			natsRunner.Start()

			// retries every second; can fail under load, so give it a bit
			Eventually(metricsServer, 3*time.Second).Should(gbytes.Say("started"))
		})
	})
})
<commit_msg>fix race for real<commit_after>package main_test

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"testing"
	"time"

	"github.com/apcera/nats"
	"github.com/cloudfoundry-incubator/metricz/collector_registrar"
	"github.com/cloudfoundry/gunk/natsrunner"
	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

func TestRuntimeMetricsServer(t *testing.T) {
	RegisterFailHandler(Fail)
	SetDefaultConsistentlyDuration(time.Second)
	SetDefaultConsistentlyPollingInterval(100 * time.Millisecond)
	SetDefaultEventuallyPollingInterval(100 * time.Millisecond)
	RunSpecs(t, "RuntimeMetricsServer Suite")
}

func NewMetricServer(binPath string, metricsPort, etcdPort, natsPort int) *ginkgomon.Runner {
	return ginkgomon.New(ginkgomon.Config{
		Name:          "metrics",
		AnsiColorCode: "61m",
		Command: exec.Command(
			binPath,
			"-port", fmt.Sprintf("%d", metricsPort),
			"-etcdCluster", fmt.Sprintf("http://127.0.0.1:%d", etcdPort),
			"-natsAddresses", fmt.Sprintf("127.0.0.1:%d", natsPort),
			"-index", "5",
			"-username", "the-username",
			"-password", "the-password",
		),
	})
}

var _ = Describe("Main", func() {
	var metricsPort int
	var natsPort int
	var etcdPort int
	var natsRunner *natsrunner.NATSRunner
	var etcdRunner *etcdstorerunner.ETCDClusterRunner
	var metricsServerPath string
	var metricsServer *ginkgomon.Runner
	var metricsProcess ifrit.Process

	BeforeSuite(func() {
		var err error
		metricsServerPath, err = gexec.Build("github.com/cloudfoundry-incubator/runtime-metrics-server")
		Ω(err).ShouldNot(HaveOccurred())
		metricsPort = 5678 + GinkgoParallelNode()
		etcdPort = 5001 + GinkgoParallelNode()
		natsPort = 4228 + GinkgoParallelNode()
	})

	AfterSuite(func() {
		gexec.CleanupBuildArtifacts()
	})

	BeforeEach(func() {
		etcdRunner = etcdstorerunner.NewETCDClusterRunner(etcdPort, 1)
		natsRunner = natsrunner.NewNATSRunner(natsPort)
		metricsServer = NewMetricServer(metricsServerPath, metricsPort, etcdPort, natsPort)
	})

	JustBeforeEach(func() {
		metricsProcess = ifrit.Envoke(metricsServer)
	})

	AfterEach(func() {
		defer func() {
			metricsProcess.Signal(os.Kill)
			Eventually(metricsProcess.Wait(), time.Second).Should(Receive())
		}()

		metricsProcess.Signal(os.Interrupt)
		Eventually(metricsProcess.Wait(), 5*time.Second).Should(Receive(BeNil()))
	})

	Context("When nats is avaialble", func() {
		BeforeEach(func() {
			natsRunner.Start()
			etcdRunner.Start()
		})

		AfterEach(func() {
			natsRunner.Stop()
			etcdRunner.Stop()
		})

		It("reports started", func() {
			Eventually(metricsServer).Should(gbytes.Say("started"))
		})

		Context("and we are subsribed to component announcements", func() {
			var announcement collector_registrar.AnnounceComponentMessage
			var announcements <-chan collector_registrar.AnnounceComponentMessage

			BeforeEach(func() {
				receivedAnnouncements := make(chan collector_registrar.AnnounceComponentMessage)

				announcements = receivedAnnouncements

				natsRunner.MessageBus.Subscribe("vcap.component.announce", func(message *nats.Msg) {
					var registration collector_registrar.AnnounceComponentMessage

					err := json.Unmarshal(message.Data, &registration)
					Ω(err).ShouldNot(HaveOccurred())

					receivedAnnouncements <- registration
				})
			})

			JustBeforeEach(func() {
				Eventually(announcements).Should(Receive(&announcement))
			})

			It("reports the correct index", func() {
				Ω(announcement.Index).Should(Equal(uint(5)))
			})

			It("listens on /varz of the reported host", func() {
				Eventually(func() error {
					conn, err := net.Dial("tcp", announcement.Host)
					if err != nil {
						return nil
					}
					defer conn.Close()
					return err
				}).ShouldNot(HaveOccurred())

				req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/varz", announcement.Host), nil)
				Ω(err).ShouldNot(HaveOccurred())
				req.SetBasicAuth("the-username", "the-password")

				resp, err := http.DefaultClient.Do(req)
				Ω(err).ShouldNot(HaveOccurred())
				Ω(resp.StatusCode).Should(Equal(200))
			})
		})
	})

	Context("When nats is not avaiable", func() {
		BeforeEach(func() {
			etcdRunner.Start()
		})

		AfterEach(func() {
			etcdRunner.Stop()
		})

		It("should not start server", func() {
			Consistently(metricsServer).ShouldNot(gbytes.Say("started"))
		})

		It("does not exit", func() {
			Consistently(metricsProcess.Wait()).ShouldNot(Receive())
		})
	})

	Context("When nats not available at first, but eventually becomes available", func() {
		BeforeEach(func() {
			etcdRunner.Start()
		})

		AfterEach(func() {
			natsRunner.Stop()
			etcdRunner.Stop()
		})

		It("should not start server until nats becomes avaialble", func() {
			Consistently(metricsServer).ShouldNot(gbytes.Say("started"))

			natsRunner.Start()

			// retries every second; can fail under load, so give it a bit
			Eventually(metricsServer, 3*time.Second).Should(gbytes.Say("started"))
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
    "testing"
)

// 0
// 1 2
// 4
// 3 5

func TestExtractNodes(t *testing.T) {
    in := "INSERT INTO `foo` VALUES (0,'name 0',null),(1,'name 1',0),(2,'name 2',0),(3,'name 3',4),(4,'name 4',1),(5,'name 5',4)"

    expectedNodes := []RawAdjacencyTreeNode{
        RawAdjacencyTreeNode{Id: "0", ParentId: "null"},
        RawAdjacencyTreeNode{Id: "1", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "2", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "3", ParentId: "4"},
        RawAdjacencyTreeNode{Id: "4", ParentId: "1"},
        RawAdjacencyTreeNode{Id: "5", ParentId: "4"},
    }

    expectedOut := RawAdjacencyTreeNodes{Nodes: expectedNodes}

    actualOut := extractNodes(in)

    if !actualOut.equalTo(expectedOut) {
        t.Errorf("Expected %v, got %v", expectedOut, actualOut)
    }
}

func TestBuildLinkedNodes(t *testing.T) {
    in := []RawAdjacencyTreeNode{
        RawAdjacencyTreeNode{Id: "0", ParentId: "null"},
        RawAdjacencyTreeNode{Id: "1", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "2", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "3", ParentId: "4"},
        RawAdjacencyTreeNode{Id: "4", ParentId: "1"},
        RawAdjacencyTreeNode{Id: "5", ParentId: "4"},
    }

    elem3 := LinkedAdjacencyTreeNode{Id: "3"}
    elem5 := LinkedAdjacencyTreeNode{Id: "5"}
    elem4 := LinkedAdjacencyTreeNode{Id: "4", Children: []*LinkedAdjacencyTreeNode{&elem3, &elem5}}

    elem1 := LinkedAdjacencyTreeNode{Id: "1", Children: []*LinkedAdjacencyTreeNode{&elem4}}
    elem2 := LinkedAdjacencyTreeNode{Id: "2"}

    elem0 := LinkedAdjacencyTreeNode{Id: "0", Children: []*LinkedAdjacencyTreeNode{&elem1, &elem2}}

    expectedOut := elem0

    actualOut := buildLinkedNodes(in)

    if !actualOut.equalTo(expectedOut) {
        t.Errorf("Expected %v, got %v", expectedOut, actualOut)
    }
}<commit_msg>Some testing niceties added<commit_after>package main

import (
    "testing"
)

/*
      0
     / \
    1   2
   /
  4
 / \
3   5
*/

func TestExtractNodes(t *testing.T) {
    in := "INSERT INTO `foo` VALUES (0,'name 0',null),(1,'name 1',0),(2,'name 2',0),(3,'name 3',4),(4,'name 4',1),(5,'name 5',4)"

    expectedNodes := []RawAdjacencyTreeNode{
        RawAdjacencyTreeNode{Id: "0", ParentId: "null"},
        RawAdjacencyTreeNode{Id: "1", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "2", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "3", ParentId: "4"},
        RawAdjacencyTreeNode{Id: "4", ParentId: "1"},
        RawAdjacencyTreeNode{Id: "5", ParentId: "4"},
    }

    expectedOut := RawAdjacencyTreeNodes{Nodes: expectedNodes}

    actualOut := extractNodes(in)

    if !actualOut.equalTo(expectedOut) {
        t.Errorf("Expected %v, got %v", expectedOut, actualOut)
    }
}

func TestBuildLinkedNodes(t *testing.T) {
    in := []RawAdjacencyTreeNode{
        RawAdjacencyTreeNode{Id: "0", ParentId: "null"},
        RawAdjacencyTreeNode{Id: "1", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "2", ParentId: "0"},
        RawAdjacencyTreeNode{Id: "3", ParentId: "4"},
        RawAdjacencyTreeNode{Id: "4", ParentId: "1"},
        RawAdjacencyTreeNode{Id: "5", ParentId: "4"},
    }

    elem3 := LinkedAdjacencyTreeNode{Id: "3"}
    elem5 := LinkedAdjacencyTreeNode{Id: "5"}
    elem4 := LinkedAdjacencyTreeNode{Id: "4", Children: []*LinkedAdjacencyTreeNode{&elem3, &elem5}}

    elem1 := LinkedAdjacencyTreeNode{Id: "1", Children: []*LinkedAdjacencyTreeNode{&elem4}}
    elem2 := LinkedAdjacencyTreeNode{Id: "2"}

    elem0 := LinkedAdjacencyTreeNode{Id: "0", Children: []*LinkedAdjacencyTreeNode{&elem1, &elem2}}

    expectedOut := elem0

    actualOut := buildLinkedNodes(in)

    if !actualOut.equalTo(expectedOut) {
        t.Errorf("Expected %v, got %v", expectedOut, actualOut)
    }
}<|endoftext|>


<commit_before>package main_test

import (
	"errors"
	"regexp"
	"strconv"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	plugin "hub.jazz.net/git/bluemixgarage/cf-blue-green-deploy"

	"github.com/cloudfoundry/cli/plugin/fakes"
)

var _ = Describe("BGD Plugin", func() {
	Describe("the DeleteOldAppVersions function", func() {
		Context("when there is an old version deployed", func() {
			var connection *fakes.FakeCliConnection
			apps := []plugin.Application{{Name: "app-name-20150326110000-old"}}

			BeforeEach(func() {
				connection = &fakes.FakeCliConnection{}
			})

			It("deletes the old version", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				p.DeleteOldAppVersions("app-name", apps)

				Expect(strings.Join(connection.CliCommandArgsForCall(0), " ")).
					To(Equal("delete app-name-20150326110000-old -f -r"))
			})

			Context("when the deletion of an old app fails", func() {
				BeforeEach(func() {
					connection.CliCommandStub = func(args ...string) ([]string, error) {
						return nil, errors.New("failed to delete app")
					}
				})

				It("returns an error", func() {
					p := plugin.BlueGreenDeployPlugin{Connection: connection}
					err := p.DeleteOldAppVersions("app-name", apps)

					Expect(err).To(HaveOccurred())
				})
			})
		})

		Context("when there is no old version deployed", func() {
			var connection *fakes.FakeCliConnection
			apps := []plugin.Application{}

			BeforeEach(func() {
				connection = &fakes.FakeCliConnection{}
			})

			It("succeeds", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				err := p.DeleteOldAppVersions("app-name", apps)
				Expect(err).ToNot(HaveOccurred())
			})

			It("deletes nothing", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				p.DeleteOldAppVersions("app-name", apps)
				Expect(connection.CliCommandCallCount()).To(Equal(0))
			})
		})
	})

	Describe("the PushNewAppVersion function", func() {
		It("pushes an app with the timestamp appended to its name", func() {
			connection := &fakes.FakeCliConnection{}

			p := plugin.BlueGreenDeployPlugin{Connection: connection}
			p.PushNewAppVersion("app-name")

			Expect(strings.Join(connection.CliCommandArgsForCall(0), " ")).
				To(MatchRegexp(`^push app-name-\d{14}$`))
		})

		It("returns the new app name", func() {
			connection := &fakes.FakeCliConnection{}
			p := plugin.BlueGreenDeployPlugin{Connection: connection}
			newAppName, _ := p.PushNewAppVersion("app-name")

			Expect(newAppName).To(MatchRegexp(`^app-name-\d{14}$`))
		})

		Context("when the push fails", func() {
			var connection *fakes.FakeCliConnection

			BeforeEach(func() {
				connection = &fakes.FakeCliConnection{}
				connection.CliCommandStub = func(args ...string) ([]string, error) {
					return nil, errors.New("failed to push app")
				}
			})

			It("returns an error", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				_, err := p.PushNewAppVersion("app-name")

				Expect(err).To(MatchError("failed to push app"))
			})
		})
	})

	Describe("the MapRoutesFromPreviousApp function", func() {
		Context("when there was an app previously pushed", func() {
			previousApp := plugin.Application{
				Name: "foo",
				Routes: []plugin.Route{
					{Host: "foo", Domain: plugin.Domain{Name: "example.com"}},
				},
			}

			It("maps the routes of the previous app to the new app", func() {
				connection := &fakes.FakeCliConnection{}
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				p.MapRoutesFromPreviousApp("foo-12345", previousApp)

				Expect(strings.Join(connection.CliCommandArgsForCall(0), " ")).
					To(Equal("map-route foo-12345 example.com -n foo"))
			})
		})
	})

	Describe("integration test script", func() {
		Context("when integration test flag is not provided", func() {
			It("returns empty string", func() {
				args := []string{"blue-green-deploy", "appName"}
				Expect(plugin.ExtractIntegrationTestScript(args)).To(Equal(""))
			})
		})

		Context("when integration test flag provided", func() {
			It("returns flag value", func() {
				args := []string{"blue-green-deploy", "appName", "--integration-test=script/test"}
				Expect(plugin.ExtractIntegrationTestScript(args)).To(Equal("script/test"))
			})
		})
	})

	Describe("integration test runner", func() {
		It("returns stdout", func() {
			out, _ := plugin.RunIntegrationTestScript("test/support/integration-test-script", "app.mybluemix.net")
			Expect(out).To(ContainSubstring("STDOUT"))
		})

		It("returns stderr", func() {
			out, _ := plugin.RunIntegrationTestScript("test/support/integration-test-script", "app.mybluemix.net")
			Expect(out).To(ContainSubstring("STDERR"))
		})

		It("passes app FQDN as first argument", func() {
			out, _ := plugin.RunIntegrationTestScript("test/support/integration-test-script", "app.mybluemix.net")
			Expect(out).To(ContainSubstring("App FQDN is: app.mybluemix.net"))
		})

		Context("when script doesn't exist", func() {
			It("fails with useful error", func() {
				_, err := plugin.RunIntegrationTestScript("inexistent-integration-test-script", "app.mybluemix.net")
				Expect(err.Error()).To(ContainSubstring("executable file not found in $PATH"))
			})
		})
	})

	Describe("old app filter", func() {
		Context("when there are 2 old versions and 1 non-old version", func() {
			appList := []plugin.Application{
				{Name: "foo-20150408114041-old"},
				{Name: "foo-20141234567348-old"},
				{Name: "foo-20163453473845"},
				{Name: "bar-foo-20141234567348-old"},
				{Name: "foo-20141234567348-older"},
			}

			It("returns all apps that have the same name, with a valid timestamp and -old suffix", func() {
				Expect(plugin.FilterOldApps("foo", appList)).To(ContainElement(appList[0]))
				Expect(plugin.FilterOldApps("foo", appList)).To(ContainElement(appList[1]))
			})

			It("doesn't return any apps that don't have a -old suffix", func() {
				Expect(plugin.FilterOldApps("foo", appList)).ToNot(ContainElement(appList[2]))
			})

			It("doesn't return elements that have an additional prefix before the app name", func() {
				Expect(plugin.FilterOldApps("foo", appList)).ToNot(ContainElement(appList[3]))
			})

			It("doesn't return elements that have an additional suffix after -old", func() {
				Expect(plugin.FilterOldApps("foo", appList)).ToNot(ContainElement(appList[4]))
			})
		})
	})

	Describe("app name generator", func() {
		generated := plugin.GenerateAppName("foo")

		It("uses the passed name as a prefix", func() {
			Expect(generated).To(HavePrefix("foo"))
		})

		It("uses a timestamp as a suffix", func() {
			now, _ := strconv.Atoi(time.Now().Format("20060102150405"))
			genTimestamp, _ := strconv.Atoi(regexp.MustCompile(`\d{14}`).FindString(generated))

			Expect(now - genTimestamp).To(BeNumerically("<", 5))
		})
	})
})
<commit_msg>More concise<commit_after>package main_test

import (
	"errors"
	"regexp"
	"strconv"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	plugin "hub.jazz.net/git/bluemixgarage/cf-blue-green-deploy"

	"github.com/cloudfoundry/cli/plugin/fakes"
)

var _ = Describe("BGD Plugin", func() {
	Describe("the DeleteOldAppVersions function", func() {
		Context("when there is an old version deployed", func() {
			var connection *fakes.FakeCliConnection
			apps := []plugin.Application{{Name: "app-name-20150326110000-old"}}

			BeforeEach(func() {
				connection = &fakes.FakeCliConnection{}
			})

			It("deletes the old version", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				p.DeleteOldAppVersions("app-name", apps)

				Expect(strings.Join(connection.CliCommandArgsForCall(0), " ")).
					To(Equal("delete app-name-20150326110000-old -f -r"))
			})

			Context("when the deletion of an old app fails", func() {
				BeforeEach(func() {
					connection.CliCommandStub = func(args ...string) ([]string, error) {
						return nil, errors.New("failed to delete app")
					}
				})

				It("returns an error", func() {
					p := plugin.BlueGreenDeployPlugin{Connection: connection}
					err := p.DeleteOldAppVersions("app-name", apps)

					Expect(err).To(HaveOccurred())
				})
			})
		})

		Context("when there is no old version deployed", func() {
			var connection *fakes.FakeCliConnection
			apps := []plugin.Application{}

			BeforeEach(func() {
				connection = &fakes.FakeCliConnection{}
			})

			It("succeeds", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				err := p.DeleteOldAppVersions("app-name", apps)
				Expect(err).ToNot(HaveOccurred())
			})

			It("deletes nothing", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				p.DeleteOldAppVersions("app-name", apps)
				Expect(connection.CliCommandCallCount()).To(Equal(0))
			})
		})
	})

	Describe("the PushNewAppVersion function", func() {
		It("pushes an app with the timestamp appended to its name", func() {
			connection := &fakes.FakeCliConnection{}

			p := plugin.BlueGreenDeployPlugin{Connection: connection}
			p.PushNewAppVersion("app-name")

			Expect(strings.Join(connection.CliCommandArgsForCall(0), " ")).
				To(MatchRegexp(`^push app-name-\d{14}$`))
		})

		It("returns the new app name", func() {
			connection := &fakes.FakeCliConnection{}
			p := plugin.BlueGreenDeployPlugin{Connection: connection}
			newAppName, _ := p.PushNewAppVersion("app-name")

			Expect(newAppName).To(MatchRegexp(`^app-name-\d{14}$`))
		})

		Context("when the push fails", func() {
			var connection *fakes.FakeCliConnection

			BeforeEach(func() {
				connection = &fakes.FakeCliConnection{}
				connection.CliCommandStub = func(args ...string) ([]string, error) {
					return nil, errors.New("failed to push app")
				}
			})

			It("returns an error", func() {
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				_, err := p.PushNewAppVersion("app-name")

				Expect(err).To(MatchError("failed to push app"))
			})
		})
	})

	Describe("the MapRoutesFromPreviousApp function", func() {
		Context("when there was an app previously pushed", func() {
			previousApp := plugin.Application{
				Name: "foo",
				Routes: []plugin.Route{
					{Host: "foo", Domain: plugin.Domain{Name: "example.com"}},
				},
			}

			It("maps the routes of the previous app to the new app", func() {
				connection := &fakes.FakeCliConnection{}
				p := plugin.BlueGreenDeployPlugin{Connection: connection}
				p.MapRoutesFromPreviousApp("foo-12345", previousApp)

				Expect(strings.Join(connection.CliCommandArgsForCall(0), " ")).
					To(Equal("map-route foo-12345 example.com -n foo"))
			})
		})
	})

	Describe("integration test script", func() {
		Context("when integration test flag is not provided", func() {
			It("returns empty string", func() {
				args := []string{"blue-green-deploy", "appName"}
				Expect(plugin.ExtractIntegrationTestScript(args)).To(Equal(""))
			})
		})

		Context("when integration test flag provided", func() {
			It("returns flag value", func() {
				args := []string{"blue-green-deploy", "appName", "--integration-test=script/test"}
				Expect(plugin.ExtractIntegrationTestScript(args)).To(Equal("script/test"))
			})
		})
	})

	Describe("integration test runner", func() {
		It("returns stdout", func() {
			out, _ := plugin.RunIntegrationTestScript("test/support/integration-test-script", "app.mybluemix.net")
			Expect(out).To(ContainSubstring("STDOUT"))
		})

		It("returns stderr", func() {
			out, _ := plugin.RunIntegrationTestScript("test/support/integration-test-script", "app.mybluemix.net")
			Expect(out).To(ContainSubstring("STDERR"))
		})

		It("passes app FQDN as first argument", func() {
			out, _ := plugin.RunIntegrationTestScript("test/support/integration-test-script", "app.mybluemix.net")
			Expect(out).To(ContainSubstring("App FQDN is: app.mybluemix.net"))
		})

		Context("when script doesn't exist", func() {
			It("fails with useful error", func() {
				_, err := plugin.RunIntegrationTestScript("inexistent-integration-test-script", "app.mybluemix.net")
				Expect(err.Error()).To(ContainSubstring("executable file not found"))
			})
		})
	})

	Describe("old app filter", func() {
		Context("when there are 2 old versions and 1 non-old version", func() {
			appList := []plugin.Application{
				{Name: "foo-20150408114041-old"},
				{Name: "foo-20141234567348-old"},
				{Name: "foo-20163453473845"},
				{Name: "bar-foo-20141234567348-old"},
				{Name: "foo-20141234567348-older"},
			}

			It("returns all apps that have the same name, with a valid timestamp and -old suffix", func() {
				Expect(plugin.FilterOldApps("foo", appList)).To(ContainElement(appList[0]))
				Expect(plugin.FilterOldApps("foo", appList)).To(ContainElement(appList[1]))
			})

			It("doesn't return any apps that don't have a -old suffix", func() {
				Expect(plugin.FilterOldApps("foo", appList)).ToNot(ContainElement(appList[2]))
			})

			It("doesn't return elements that have an additional prefix before the app name", func() {
				Expect(plugin.FilterOldApps("foo", appList)).ToNot(ContainElement(appList[3]))
			})

			It("doesn't return elements that have an additional suffix after -old", func() {
				Expect(plugin.FilterOldApps("foo", appList)).ToNot(ContainElement(appList[4]))
			})
		})
	})

	Describe("app name generator", func() {
		generated := plugin.GenerateAppName("foo")

		It("uses the passed name as a prefix", func() {
			Expect(generated).To(HavePrefix("foo"))
		})

		It("uses a timestamp as a suffix", func() {
			now, _ := strconv.Atoi(time.Now().Format("20060102150405"))
			genTimestamp, _ := strconv.Atoi(regexp.MustCompile(`\d{14}`).FindString(generated))

			Expect(now - genTimestamp).To(BeNumerically("<", 5))
		})
	})
})
<|endoftext|>