<commit_before>package commands

import (
	"errors"
	"fmt"
	"io"

	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	blockservice "github.com/ipfs/go-ipfs/blockservice"
	cmds "github.com/ipfs/go-ipfs/commands"
	files "github.com/ipfs/go-ipfs/commands/files"
	core "github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/core/coreunix"
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	dag "github.com/ipfs/go-ipfs/merkledag"
	dagtest "github.com/ipfs/go-ipfs/merkledag/test"
	mfs "github.com/ipfs/go-ipfs/mfs"
	ft "github.com/ipfs/go-ipfs/unixfs"

	u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util"
	"gx/ipfs/QmeWjRodbcZFKe5tMN7poEx3izym6osrLSnTLf9UjJZBbs/pb"
)

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

const (
	quietOptionName       = "quiet"
	quieterOptionName     = "quieter"
	silentOptionName      = "silent"
	progressOptionName    = "progress"
	trickleOptionName     = "trickle"
	wrapOptionName        = "wrap-with-directory"
	hiddenOptionName      = "hidden"
	onlyHashOptionName    = "only-hash"
	chunkerOptionName     = "chunker"
	pinOptionName         = "pin"
	rawLeavesOptionName   = "raw-leaves"
	noCopyOptionName      = "nocopy"
	fstoreCacheOptionName = "fscache"
)

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(quieterOptionName, "Q", "Write only final hash."),
		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),
		cmds.BoolOption(rawLeavesOptionName, "Use raw blocks for leaf nodes. (experimental)"),
		cmds.BoolOption(noCopyOptionName, "Add the file using filestore. (experimental)"),
		cmds.BoolOption(fstoreCacheOptionName, "Check the filestore for pre-existing blocks. (experimental)"),
	},
	PreRun: func(req cmds.Request) error {
		quiet, _, _ := req.Option(quietOptionName).Bool()
		quieter, _, _ := req.Option(quieterOptionName).Bool()
		quiet = quiet || quieter

		silent, _, _ := req.Option(silentOptionName).Bool()

		if quiet || silent {
			return nil
		}

		// ipfs cli progress bar defaults to true unless quiet or silent is used
		_, found, _ := req.Option(progressOptionName).Bool()
		if !found {
			req.SetOption(progressOptionName, true)
		}

		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
		}

		cfg, err := n.Repo.Config()
		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()
		rawblks, rbset, _ := req.Option(rawLeavesOptionName).Bool()
		nocopy, _, _ := req.Option(noCopyOptionName).Bool()
		fscache, _, _ := req.Option(fstoreCacheOptionName).Bool()

		if nocopy && !cfg.Experimental.FilestoreEnabled {
			res.SetError(errors.New("filestore is not enabled, see https://git.io/vy4XN"),
				cmds.ErrClient)
			return
		}

		if nocopy && !rbset {
			rawblks = true
		}

		if nocopy && !rawblks {
			res.SetError(fmt.Errorf("nocopy option requires '--raw-leaves' to be enabled as well"), cmds.ErrNormal)
			return
		}

		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
		}

		addblockstore := n.Blockstore
		if !(fscache || nocopy) {
			addblockstore = bstore.NewGCBlockstore(n.BaseBlocks, n.GCLocker)
		}

		exch := n.Exchange
		local, _, _ := req.Option("local").Bool()
		if local {
			exch = offline.Exchange(addblockstore)
		}

		bserv := blockservice.New(addblockstore, exch)
		dserv := dag.NewDAGService(bserv)

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

		fileAdder, err := coreunix.NewAdder(req.Context(), n.Pinning, n.Blockstore, dserv)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		fileAdder.Out = outChan
		fileAdder.Chunker = chunker
		fileAdder.Progress = progress
		fileAdder.Hidden = hidden
		fileAdder.Trickle = trickle
		fileAdder.Wrap = wrap
		fileAdder.Pin = dopin
		fileAdder.Silent = silent
		fileAdder.RawLeaves = rawblks
		fileAdder.NoCopy = nocopy

		if hash {
			md := dagtest.Mock()
			mr, err := mfs.NewRoot(req.Context(), md, ft.EmptyDirNode(), nil)
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

			fileAdder.SetMfsRoot(mr)
		}

		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, _, _ := req.Option(quietOptionName).Bool()
		quieter, _, _ := req.Option(quieterOptionName).Bool()
		quiet = quiet || quieter

		progress, _, _ := req.Option(progressOptionName).Bool()

		var bar *pb.ProgressBar
		if progress {
			bar = pb.New64(0).SetUnits(pb.U_BYTES)
			bar.ManualUpdate = true
			bar.ShowTimeLeft = false
			bar.ShowPercent = false
			bar.Output = res.Stderr()
			bar.Start()
		}

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

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

	LOOP:
		for {
			select {
			case out, ok := <-outChan:
				if !ok {
					if quieter {
						fmt.Fprintln(res.Stdout(), lastHash)
					}
					break LOOP
				}
				output := out.(*coreunix.AddedObject)
				if len(output.Hash) > 0 {
					lastHash = output.Hash
					if quieter {
						continue
					}

					if progress {
						// 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 !progress {
						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 progress {
					bar.Update()
				}
			case size := <-sizeChan:
				if progress {
					bar.Total = size
					bar.ShowPercent = true
					bar.ShowBar = true
					bar.ShowTimeLeft = true
				}
			case <-req.Context().Done():
				res.SetError(req.Context().Err(), cmds.ErrNormal)
				return
			}
		}
	},
	Type: coreunix.AddedObject{},
}
<commit_msg>adder: fix bug where errors from calls to NewAdder caused daemon to hang<commit_after>package commands

import (
	"errors"
	"fmt"
	"io"

	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	blockservice "github.com/ipfs/go-ipfs/blockservice"
	cmds "github.com/ipfs/go-ipfs/commands"
	files "github.com/ipfs/go-ipfs/commands/files"
	core "github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/core/coreunix"
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	dag "github.com/ipfs/go-ipfs/merkledag"
	dagtest "github.com/ipfs/go-ipfs/merkledag/test"
	mfs "github.com/ipfs/go-ipfs/mfs"
	ft "github.com/ipfs/go-ipfs/unixfs"

	u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util"
	"gx/ipfs/QmeWjRodbcZFKe5tMN7poEx3izym6osrLSnTLf9UjJZBbs/pb"
)

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

const (
	quietOptionName       = "quiet"
	quieterOptionName     = "quieter"
	silentOptionName      = "silent"
	progressOptionName    = "progress"
	trickleOptionName     = "trickle"
	wrapOptionName        = "wrap-with-directory"
	hiddenOptionName      = "hidden"
	onlyHashOptionName    = "only-hash"
	chunkerOptionName     = "chunker"
	pinOptionName         = "pin"
	rawLeavesOptionName   = "raw-leaves"
	noCopyOptionName      = "nocopy"
	fstoreCacheOptionName = "fscache"
)

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(quieterOptionName, "Q", "Write only final hash."),
		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),
		cmds.BoolOption(rawLeavesOptionName, "Use raw blocks for leaf nodes. (experimental)"),
		cmds.BoolOption(noCopyOptionName, "Add the file using filestore. (experimental)"),
		cmds.BoolOption(fstoreCacheOptionName, "Check the filestore for pre-existing blocks. (experimental)"),
	},
	PreRun: func(req cmds.Request) error {
		quiet, _, _ := req.Option(quietOptionName).Bool()
		quieter, _, _ := req.Option(quieterOptionName).Bool()
		quiet = quiet || quieter

		silent, _, _ := req.Option(silentOptionName).Bool()

		if quiet || silent {
			return nil
		}

		// ipfs cli progress bar defaults to true unless quiet or silent is used
		_, found, _ := req.Option(progressOptionName).Bool()
		if !found {
			req.SetOption(progressOptionName, true)
		}

		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
		}

		cfg, err := n.Repo.Config()
		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()
		rawblks, rbset, _ := req.Option(rawLeavesOptionName).Bool()
		nocopy, _, _ := req.Option(noCopyOptionName).Bool()
		fscache, _, _ := req.Option(fstoreCacheOptionName).Bool()

		if nocopy && !cfg.Experimental.FilestoreEnabled {
			res.SetError(errors.New("filestore is not enabled, see https://git.io/vy4XN"),
				cmds.ErrClient)
			return
		}

		if nocopy && !rbset {
			rawblks = true
		}

		if nocopy && !rawblks {
			res.SetError(fmt.Errorf("nocopy option requires '--raw-leaves' to be enabled as well"), cmds.ErrNormal)
			return
		}

		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
		}

		addblockstore := n.Blockstore
		if !(fscache || nocopy) {
			addblockstore = bstore.NewGCBlockstore(n.BaseBlocks, n.GCLocker)
		}

		exch := n.Exchange
		local, _, _ := req.Option("local").Bool()
		if local {
			exch = offline.Exchange(addblockstore)
		}

		bserv := blockservice.New(addblockstore, exch)
		dserv := dag.NewDAGService(bserv)

		fileAdder, err := coreunix.NewAdder(req.Context(), n.Pinning, n.Blockstore, dserv)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

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

		fileAdder.Out = outChan
		fileAdder.Chunker = chunker
		fileAdder.Progress = progress
		fileAdder.Hidden = hidden
		fileAdder.Trickle = trickle
		fileAdder.Wrap = wrap
		fileAdder.Pin = dopin
		fileAdder.Silent = silent
		fileAdder.RawLeaves = rawblks
		fileAdder.NoCopy = nocopy

		if hash {
			md := dagtest.Mock()
			mr, err := mfs.NewRoot(req.Context(), md, ft.EmptyDirNode(), nil)
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

			fileAdder.SetMfsRoot(mr)
		}

		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, _, _ := req.Option(quietOptionName).Bool()
		quieter, _, _ := req.Option(quieterOptionName).Bool()
		quiet = quiet || quieter

		progress, _, _ := req.Option(progressOptionName).Bool()

		var bar *pb.ProgressBar
		if progress {
			bar = pb.New64(0).SetUnits(pb.U_BYTES)
			bar.ManualUpdate = true
			bar.ShowTimeLeft = false
			bar.ShowPercent = false
			bar.Output = res.Stderr()
			bar.Start()
		}

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

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

	LOOP:
		for {
			select {
			case out, ok := <-outChan:
				if !ok {
					if quieter {
						fmt.Fprintln(res.Stdout(), lastHash)
					}
					break LOOP
				}
				output := out.(*coreunix.AddedObject)
				if len(output.Hash) > 0 {
					lastHash = output.Hash
					if quieter {
						continue
					}

					if progress {
						// 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 !progress {
						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 progress {
					bar.Update()
				}
			case size := <-sizeChan:
				if progress {
					bar.Total = size
					bar.ShowPercent = true
					bar.ShowBar = true
					bar.ShowTimeLeft = true
				}
			case <-req.Context().Done():
				res.SetError(req.Context().Err(), cmds.ErrNormal)
				return
			}
		}
	},
	Type: coreunix.AddedObject{},
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

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

func TestCreateServer(t *testing.T) {
	client = &HTTPClient{
		http: http.DefaultClient,
	}

	router := mux.NewRouter()
	router.HandleFunc("/TestCreateServer/servers", func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		assert.NoError(t, err)
		assert.NotNil(t, body)
		t.Log(string(body))

		assert.Equal(t, `{"server":{"id":"","name":"test","imageRef":"image","flavorRef":"flavor","user_data":"blabla\u0008labla","metadata":{"worker":"test"},"networks":[{"uuid":"network","fixed_ip":"192.168.0.1"}],"links":null,"status":"","key_name":"","addresses":null,"updated":"","personality":[{"path":"/worker","contents":"dGhpcyBpcyB0aGUgY29udGVudA=="}]}}`, string(body))

	})
	http.Handle("/TestCreateServer/", router)

	s := httptest.NewServer(router)
	defer s.Close()
	w := httptest.NewRecorder()

	p := []*File{
		&File{
			Path:     "/worker",
			Contents: []byte("this is the content"),
		},
	}

	err := createServer(s.URL+"/TestCreateServer", "", "test", "image", "flavor", "network", "192.168.0.1", "blabla\blabla", p)
	assert.NoError(t, err)

	assert.NotZero(t, w.Code)

}
<commit_msg>fix (hatchery): fix openstack test<commit_after>package main

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/facebookgo/httpcontrol"
	"github.com/gorilla/mux"
	"github.com/stretchr/testify/assert"
)

func TestCreateServer(t *testing.T) {
	client = &http.Client{
		Transport: &httpcontrol.Transport{
			RequestTimeout: 10 * time.Second,
			MaxTries:       5,
		},
	}

	router := mux.NewRouter()
	router.HandleFunc("/TestCreateServer/servers", func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		assert.NoError(t, err)
		assert.NotNil(t, body)
		t.Log(string(body))

		assert.Equal(t, `{"server":{"id":"","name":"test","imageRef":"image","flavorRef":"flavor","user_data":"blabla\u0008labla","metadata":{"worker":"test"},"networks":[{"uuid":"network","fixed_ip":"192.168.0.1"}],"links":null,"status":"","key_name":"","addresses":null,"updated":"","personality":[{"path":"/worker","contents":"dGhpcyBpcyB0aGUgY29udGVudA=="}]}}`, string(body))

	})
	http.Handle("/TestCreateServer/", router)

	s := httptest.NewServer(router)
	defer s.Close()
	w := httptest.NewRecorder()

	p := []*File{
		&File{
			Path:     "/worker",
			Contents: []byte("this is the content"),
		},
	}

	err := createServer(s.URL+"/TestCreateServer", "", "test", "image", "flavor", "network", "192.168.0.1", "blabla\blabla", p)
	assert.NoError(t, err)

	assert.NotZero(t, w.Code)

}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
)

type Node interface {
	GetID() int
	GetParent() *Group
	SetParent(*Group)
}

type Group struct {
	Id       int    `json:"id"`
	Label    string `json:"label"`
	Children []int  `json:"children"`
	Parent   *Group `json:"-"`
}

func (g *Group) GetID() int {
	return g.Id
}

func (g *Group) GetParent() *Group {
	return g.Parent
}

func (g *Group) SetParent(group *Group) {
	g.Parent = group
}

func (s *Server) ListGroups() []Group {
	groups := []Group{}
	for _, g := range s.groups {
		groups = append(groups, *g)
	}
	return groups
}

func (s *Server) GroupIndexHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(s.ListGroups()); err != nil {
		panic(err)
	}
}

func (s *Server) DetachChild(g Node) error {
	parent := g.GetParent()
	if parent == nil {
		return errors.New("no parent to detach from")
	}

	id := g.GetID()

	child := -1
	for i, v := range parent.Children {
		if v == id {
			child = i
		}
	}

	if child == -1 {
		return errors.New("could not remove child from group: child does not exist")
	}

	parent.Children = append(parent.Children[:child], parent.Children[child+1:]...)

	update := struct {
		Id    int `json:"id"`
		Child int `json:"child"`
	}{
		parent.GetID(), g.GetID(),
	}

	s.websocketBroadcast(Update{Action: DELETE, Type: GROUP_CHILD, Data: update})
	return nil
}

func (s *Server) AddChildToGroup(id int, n Node) error {
	newParent, ok := s.groups[id]
	if !ok {
		return errors.New("group not found")
	}

	nid := n.GetID()
	for _, v := range newParent.Children {
		if v == nid {
			return errors.New("node already child of this group")
		}
	}

	newParent.Children = append(newParent.Children, nid)
	if n.GetParent() != nil {
		err := s.DetachChild(n)
		if err != nil {
			return err
		}
	}

	n.SetParent(newParent)

	update := struct {
		Id    int `json:"id"`
		Child int `json:"child"`
	}{
		id, nid,
	}

	s.websocketBroadcast(Update{Action: UPDATE, Type: GROUP_CHILD, Data: update})
	return nil
}

// CreateGroupHandler responds to a POST request to instantiate a new group and add it to the Server.
// Moves all of the specified children out of the parent's group and into the new group.
func (s *Server) GroupCreateHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not read request body"})
		return
	}

	var g struct {
		Group    int    `json:"group"`
		Children []int  `json:"children"`
		Label    string `json:"label"`
	}

	err = json.Unmarshal(body, &g)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not read JSON"})
		return
	}

	s.Lock()
	defer s.Unlock()

	newGroup := &Group{
		Children: g.Children,
		Label:    g.Label,
		Id:       s.GetNextID(),
	}

	if newGroup.Children == nil {
		newGroup.Children = []int{}
	}

	for _, c := range newGroup.Children {
		_, okb := s.blocks[c]
		_, okg := s.groups[c]
		if !okb && !okg {
			w.WriteHeader(http.StatusBadRequest)
			writeJSON(w, Error{"could not create group: invalid children"})
			return
		}
	}

	s.groups[newGroup.Id] = newGroup
	s.websocketBroadcast(Update{Action: CREATE, Type: GROUP, Data: newGroup})

	err = s.AddChildToGroup(g.Group, newGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	for _, c := range newGroup.Children {
		if cb, ok := s.blocks[c]; ok {
			err = s.AddChildToGroup(newGroup.Id, cb)
		}
		if cg, ok := s.groups[c]; ok {
			err = s.AddChildToGroup(newGroup.Id, cg)
		}
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			writeJSON(w, Error{err.Error()})
			return
		}
	}

	w.WriteHeader(http.StatusNoContent)
}

func (s *Server) DeleteGroup(id int) error {
	group, ok := s.groups[id]
	if !ok {
		return errors.New("could not find group to delete")
	}

	for _, c := range group.Children {
		if _, ok := s.blocks[c]; ok {
			err := s.DeleteBlock(c)
			if err != nil {
				return err
			}
		} else if _, ok := s.groups[c]; ok {
			err := s.DeleteGroup(c)
			if err != nil {
				return err
			}
		}
	}

	update := struct {
		Id int `json:"id"`
	}{
		id,
	}
	s.DetachChild(group)
	delete(s.groups, id)
	s.websocketBroadcast(Update{Action: DELETE, Type: GROUP, Data: update})
	return nil
}

func (s *Server) GroupDeleteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ids, ok := vars["id"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no ID supplied"})
		return
	}

	id, err := strconv.Atoi(ids)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	s.Lock()
	defer s.Unlock()

	err = s.DeleteGroup(id)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
func (s *Server) GroupHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupExportHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupImportHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupModifyLabelHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupModifyAllChildrenHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupModifyChildHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ids, ok := vars["id"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no ID supplied"})
		return
	}

	id, err := strconv.Atoi(ids)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	childs, ok := vars["node_id"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no ID supplied"})
		return
	}

	child, err := strconv.Atoi(childs)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	if id == child {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"cannot add group as member of itself"})
		return
	}

	s.Lock()
	defer s.Unlock()

	var n Node

	if _, ok := s.groups[id]; !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not find id"})
		return
	}

	if b, ok := s.blocks[child]; ok {
		n = b
	}
	if g, ok := s.groups[child]; ok {
		n = g
	}

	if n == nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not find id"})
		return
	}

	err = s.AddChildToGroup(id, n)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (s *Server) GroupPositionHandler(w http.ResponseWriter, r *http.Request) {
}
<commit_msg>adding group label modify handler<commit_after>package server

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
)

type Node interface {
	GetID() int
	GetParent() *Group
	SetParent(*Group)
}

type Group struct {
	Id       int    `json:"id"`
	Label    string `json:"label"`
	Children []int  `json:"children"`
	Parent   *Group `json:"-"`
}

func (g *Group) GetID() int {
	return g.Id
}

func (g *Group) GetParent() *Group {
	return g.Parent
}

func (g *Group) SetParent(group *Group) {
	g.Parent = group
}

func (s *Server) ListGroups() []Group {
	groups := []Group{}
	for _, g := range s.groups {
		groups = append(groups, *g)
	}
	return groups
}

func (s *Server) GroupIndexHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(s.ListGroups()); err != nil {
		panic(err)
	}
}

func (s *Server) DetachChild(g Node) error {
	parent := g.GetParent()
	if parent == nil {
		return errors.New("no parent to detach from")
	}

	id := g.GetID()

	child := -1
	for i, v := range parent.Children {
		if v == id {
			child = i
		}
	}

	if child == -1 {
		return errors.New("could not remove child from group: child does not exist")
	}

	parent.Children = append(parent.Children[:child], parent.Children[child+1:]...)

	update := struct {
		Id    int `json:"id"`
		Child int `json:"child"`
	}{
		parent.GetID(), g.GetID(),
	}

	s.websocketBroadcast(Update{Action: DELETE, Type: GROUP_CHILD, Data: update})
	return nil
}

func (s *Server) AddChildToGroup(id int, n Node) error {
	newParent, ok := s.groups[id]
	if !ok {
		return errors.New("group not found")
	}

	nid := n.GetID()
	for _, v := range newParent.Children {
		if v == nid {
			return errors.New("node already child of this group")
		}
	}

	newParent.Children = append(newParent.Children, nid)
	if n.GetParent() != nil {
		err := s.DetachChild(n)
		if err != nil {
			return err
		}
	}

	n.SetParent(newParent)

	update := struct {
		Id    int `json:"id"`
		Child int `json:"child"`
	}{
		id, nid,
	}

	s.websocketBroadcast(Update{Action: UPDATE, Type: GROUP_CHILD, Data: update})
	return nil
}

// CreateGroupHandler responds to a POST request to instantiate a new group and add it to the Server.
// Moves all of the specified children out of the parent's group and into the new group.
func (s *Server) GroupCreateHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not read request body"})
		return
	}

	var g struct {
		Group    int    `json:"group"`
		Children []int  `json:"children"`
		Label    string `json:"label"`
	}

	err = json.Unmarshal(body, &g)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not read JSON"})
		return
	}

	s.Lock()
	defer s.Unlock()

	newGroup := &Group{
		Children: g.Children,
		Label:    g.Label,
		Id:       s.GetNextID(),
	}

	if newGroup.Children == nil {
		newGroup.Children = []int{}
	}

	for _, c := range newGroup.Children {
		_, okb := s.blocks[c]
		_, okg := s.groups[c]
		if !okb && !okg {
			w.WriteHeader(http.StatusBadRequest)
			writeJSON(w, Error{"could not create group: invalid children"})
			return
		}
	}

	s.groups[newGroup.Id] = newGroup
	s.websocketBroadcast(Update{Action: CREATE, Type: GROUP, Data: newGroup})

	err = s.AddChildToGroup(g.Group, newGroup)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	for _, c := range newGroup.Children {
		if cb, ok := s.blocks[c]; ok {
			err = s.AddChildToGroup(newGroup.Id, cb)
		}
		if cg, ok := s.groups[c]; ok {
			err = s.AddChildToGroup(newGroup.Id, cg)
		}
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			writeJSON(w, Error{err.Error()})
			return
		}
	}

	w.WriteHeader(http.StatusNoContent)
}

func (s *Server) DeleteGroup(id int) error {
	group, ok := s.groups[id]
	if !ok {
		return errors.New("could not find group to delete")
	}

	for _, c := range group.Children {
		if _, ok := s.blocks[c]; ok {
			err := s.DeleteBlock(c)
			if err != nil {
				return err
			}
		} else if _, ok := s.groups[c]; ok {
			err := s.DeleteGroup(c)
			if err != nil {
				return err
			}
		}
	}

	update := struct {
		Id int `json:"id"`
	}{
		id,
	}
	s.DetachChild(group)
	delete(s.groups, id)
	s.websocketBroadcast(Update{Action: DELETE, Type: GROUP, Data: update})
	return nil
}

func (s *Server) GroupDeleteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ids, ok := vars["id"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no ID supplied"})
		return
	}

	id, err := strconv.Atoi(ids)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	s.Lock()
	defer s.Unlock()

	err = s.DeleteGroup(id)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
func (s *Server) GroupHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupExportHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupImportHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupModifyLabelHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not read request body"})
		return
	}

	vars := mux.Vars(r)
	ids, ok := vars["id"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no ID supplied"})
		return
	}

	id, err := strconv.Atoi(ids)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	var l string
	err = json.Unmarshal(body, &l)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not unmarshal value"})
		return
	}

	s.Lock()
	defer s.Unlock()

	g, ok := s.groups[id]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no block found"})
		return
	}

	g.Label = l

	update := struct {
		Label string `json:"label"`
		Id    int    `json:"id"`
	}{
		l, id,
	}

	s.websocketBroadcast(Update{Action: UPDATE, Type: GROUP, Data: update})

	w.WriteHeader(http.StatusNoContent)
}
func (s *Server) GroupModifyAllChildrenHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) GroupModifyChildHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ids, ok := vars["id"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no ID supplied"})
		return
	}

	id, err := strconv.Atoi(ids)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	childs, ok := vars["node_id"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"no ID supplied"})
		return
	}

	child, err := strconv.Atoi(childs)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	if id == child {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"cannot add group as member of itself"})
		return
	}

	s.Lock()
	defer s.Unlock()

	var n Node

	if _, ok := s.groups[id]; !ok {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not find id"})
		return
	}

	if b, ok := s.blocks[child]; ok {
		n = b
	}
	if g, ok := s.groups[child]; ok {
		n = g
	}

	if n == nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{"could not find id"})
		return
	}

	err = s.AddChildToGroup(id, n)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (s *Server) GroupPositionHandler(w http.ResponseWriter, r *http.Request) {
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"log"
	"testing"
	"time"
)

func TestList(t *testing.T) {
	log.Println("testing list")
	l := NewList()
	if l.GetType() != LIST {
		t.Fatal("list returns inaccurate id")
	}

	library := GetLibrary()
	blocks := map[string]*Block{
		"listGet":    NewBlock(library["listGet"]),
		"listSet":    NewBlock(library["listSet"]),
		"listAppend": NewBlock(library["listAppend"]),
		"listPop":    NewBlock(library["listPop"]),
		"listShift":  NewBlock(library["listShift"]),
		"listDump":   NewBlock(library["listDump"]),
	}

	for _, v := range blocks {
		go DummyMonitor(v.Monitor)
	}

	out := make(chan Message)
	for name, b := range blocks {
		log.Println("testing", name)
		go b.Serve()
		err := b.SetSource(l)
		if err != nil {
			t.Fatal(err)
		}
		b.Connect(0, out)
	}

	// put "foo" onto the empty list
	elementChan, err := blocks["listAppend"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	elementChan.C <- "foo"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// set the zeroth element to "bar"
	indexChan, err := blocks["listSet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	elementChan, err = blocks["listSet"].GetInput(1)
	if err != nil {
		t.Fatal(err)
	}
	indexChan.C <- 0.0
	elementChan.C <- "bar"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// get the zeroth element and makes sure it's "bar"
	indexChan, err = blocks["listGet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	indexChan.C <- 0.0
	if <-out != "bar" {
		t.Error("block did not produce expected value")
		return
	}

	// put "foo" into the front of the list
	elementChan, err = blocks["listShift"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	elementChan.C <- "foo"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// pop the last element off the list and make sure it's "bar"
	triggerChan, err := blocks["listPop"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	triggerChan.C <- true
	if <-out != "bar" {
		t.Error("block did not produce expected value")
		return
	}

	// dump the list and make sure it's ["foo"]
	triggerChan, err = blocks["listDump"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	triggerChan.C <- true
	msg := <-out
	myList := msg.([]interface{})

	if len(myList) != 1 {
		t.Error("dumped list wrong length")
		return
	}
	if myList[0] != "foo" {
		t.Error("dumped list has unexpected element")
	}
}

func TestValuePrimitive(t *testing.T) {
	log.Println("testing value primitive")
	v := NewValue()
	if v.GetType() != VALUE_PRIMITIVE {
		t.Fatal("Value source has wrong type")
	}
	library := GetLibrary()
	blocks := map[string]*Block{
		"valueGet": NewBlock(library["valueGet"]),
		"valueSet": NewBlock(library["valueSet"]),
	}

	for _, v := range blocks {
		go DummyMonitor(v.Monitor)
	}

	out := make(chan Message)
	for name, b := range blocks {
		log.Println("testing", name)
		go b.Serve()
		err := b.SetSource(v)
		if err != nil {
			t.Fatal(err)
		}
		b.Connect(0, out)
	}

	// set the value to "foo"
	valueChan, err := blocks["valueSet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	valueChan.C <- "foo"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// make sure the value was set to "foo"
	triggerChan, err := blocks["valueGet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	triggerChan.C <- true
	if <-out != "foo" {
		t.Error("block did not produce expected value")
		return
	}
}

func TestPriorityQueue(t *testing.T) {
	log.Println("testing priority queue")
	pq := NewPriorityQueue()
	if pq.GetType() != PRIORITY {
		t.Fatal("Priority Queue returns wrong type")
	}
	library := GetLibrary()
	blocks := map[string]*Block{
		"pqPush": NewBlock(library["pqPush"]),
		"pqPop":  NewBlock(library["pqPop"]),
		"pqPeek": NewBlock(library["pqPeek"]),
		"pqLen":  NewBlock(library["pqLen"]),
	}

	for _, v := range blocks {
		go DummyMonitor(v.Monitor)
	}

	out := make(chan Message)
	for name, b := range blocks {
		log.Println("testing", name)
		go b.Serve()
		err := b.SetSource(pq)
		if err != nil {
			t.Fatal(err)
		}
		b.Connect(0, out)
	}

	// connect up a channel for priorities for pqPop and pqPeek
	priorityChan := make(chan Message)

	blocks["pqPeek"].Connect(1, priorityChan)
	blocks["pqPop"].Connect(1, priorityChan)

	// push a message with the current time as priority
	p := float64(time.Now().Unix())
	in, err := blocks["pqPush"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	priority, err := blocks["pqPush"].GetInput(1)
	if err != nil {
		t.Fatal(err)
	}
	in.C <- "foo"
	priority.C <- p
	msg := <-out
	if msg != true {
		t.Fatal("pqPush did not generate expected output")
	}

	// peek at the message
	trigger, err := blocks["pqPeek"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	trigger.C <- true
	if <-out != "foo" {
		t.Fatal("pqPeek did not generate expected output")
	}
	if <-priorityChan != p {
		t.Fatal("pqPeek did not recive the expected priority")
	}

	// check the length of the queue
	lenTrigger, err := blocks["pqLen"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	lenTrigger.C <- true

	if l := <-out; l != 1.0 {
		length, ok := l.(float64)
		if !ok {
			t.Fatal("length produced something that's not a float")
		}
		t.Fatal("pqLen did not generate expected output. Expecting 1 got " + fmt.Sprint(length))
	}

	// pop the message
	trigger, err = blocks["pqPop"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	trigger.C <- true
	if <-out != "foo" {
		t.Fatal("pqPop did not generate expected output")
	}
	if <-priorityChan != p {
		t.Fatal("pqPop did not recive the expected priority")
	}

	// check the length of the queue again to make sure pop did its thing
	lenTrigger.C <- true
	if <-out != 0.0 {
		t.Fatal("pqPop did not remove the message")
	}

}
<commit_msg>anal Sprint change<commit_after>package core

import (
	"fmt"
	"log"
	"testing"
	"time"
)

func TestList(t *testing.T) {
	log.Println("testing list")
	l := NewList()
	if l.GetType() != LIST {
		t.Fatal("list returns inaccurate id")
	}

	library := GetLibrary()
	blocks := map[string]*Block{
		"listGet":    NewBlock(library["listGet"]),
		"listSet":    NewBlock(library["listSet"]),
		"listAppend": NewBlock(library["listAppend"]),
		"listPop":    NewBlock(library["listPop"]),
		"listShift":  NewBlock(library["listShift"]),
		"listDump":   NewBlock(library["listDump"]),
	}

	for _, v := range blocks {
		go DummyMonitor(v.Monitor)
	}

	out := make(chan Message)
	for name, b := range blocks {
		log.Println("testing", name)
		go b.Serve()
		err := b.SetSource(l)
		if err != nil {
			t.Fatal(err)
		}
		b.Connect(0, out)
	}

	// put "foo" onto the empty list
	elementChan, err := blocks["listAppend"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	elementChan.C <- "foo"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// set the zeroth element to "bar"
	indexChan, err := blocks["listSet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	elementChan, err = blocks["listSet"].GetInput(1)
	if err != nil {
		t.Fatal(err)
	}
	indexChan.C <- 0.0
	elementChan.C <- "bar"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// get the zeroth element and makes sure it's "bar"
	indexChan, err = blocks["listGet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	indexChan.C <- 0.0
	if <-out != "bar" {
		t.Error("block did not produce expected value")
		return
	}

	// put "foo" into the front of the list
	elementChan, err = blocks["listShift"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	elementChan.C <- "foo"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// pop the last element off the list and make sure it's "bar"
	triggerChan, err := blocks["listPop"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	triggerChan.C <- true
	if <-out != "bar" {
		t.Error("block did not produce expected value")
		return
	}

	// dump the list and make sure it's ["foo"]
	triggerChan, err = blocks["listDump"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	triggerChan.C <- true
	msg := <-out
	myList := msg.([]interface{})

	if len(myList) != 1 {
		t.Error("dumped list wrong length")
		return
	}
	if myList[0] != "foo" {
		t.Error("dumped list has unexpected element")
	}
}

func TestValuePrimitive(t *testing.T) {
	log.Println("testing value primitive")
	v := NewValue()
	if v.GetType() != VALUE_PRIMITIVE {
		t.Fatal("Value source has wrong type")
	}
	library := GetLibrary()
	blocks := map[string]*Block{
		"valueGet": NewBlock(library["valueGet"]),
		"valueSet": NewBlock(library["valueSet"]),
	}

	for _, v := range blocks {
		go DummyMonitor(v.Monitor)
	}

	out := make(chan Message)
	for name, b := range blocks {
		log.Println("testing", name)
		go b.Serve()
		err := b.SetSource(v)
		if err != nil {
			t.Fatal(err)
		}
		b.Connect(0, out)
	}

	// set the value to "foo"
	valueChan, err := blocks["valueSet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	valueChan.C <- "foo"
	if <-out != true {
		t.Error("block did not produce expected value")
		return
	}

	// make sure the value was set to "foo"
	triggerChan, err := blocks["valueGet"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	triggerChan.C <- true
	if <-out != "foo" {
		t.Error("block did not produce expected value")
		return
	}
}

func TestPriorityQueue(t *testing.T) {
	log.Println("testing priority queue")
	pq := NewPriorityQueue()
	if pq.GetType() != PRIORITY {
		t.Fatal("Priority Queue returns wrong type")
	}
	library := GetLibrary()
	blocks := map[string]*Block{
		"pqPush": NewBlock(library["pqPush"]),
		"pqPop":  NewBlock(library["pqPop"]),
		"pqPeek": NewBlock(library["pqPeek"]),
		"pqLen":  NewBlock(library["pqLen"]),
	}

	for _, v := range blocks {
		go DummyMonitor(v.Monitor)
	}

	out := make(chan Message)
	for name, b := range blocks {
		log.Println("testing", name)
		go b.Serve()
		err := b.SetSource(pq)
		if err != nil {
			t.Fatal(err)
		}
		b.Connect(0, out)
	}

	// connect up a channel for priorities for pqPop and pqPeek
	priorityChan := make(chan Message)

	blocks["pqPeek"].Connect(1, priorityChan)
	blocks["pqPop"].Connect(1, priorityChan)

	// push a message with the current time as priority
	p := float64(time.Now().Unix())
	in, err := blocks["pqPush"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	priority, err := blocks["pqPush"].GetInput(1)
	if err != nil {
		t.Fatal(err)
	}
	in.C <- "foo"
	priority.C <- p
	msg := <-out
	if msg != true {
		t.Fatal("pqPush did not generate expected output")
	}

	// peek at the message
	trigger, err := blocks["pqPeek"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	trigger.C <- true
	if <-out != "foo" {
		t.Fatal("pqPeek did not generate expected output")
	}
	if <-priorityChan != p {
		t.Fatal("pqPeek did not recive the expected priority")
	}

	// check the length of the queue
	lenTrigger, err := blocks["pqLen"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	lenTrigger.C <- true

	if l := <-out; l != 1.0 {
		length, ok := l.(float64)
		if !ok {
			t.Fatal("length produced something that's not a float")
		}
		t.Fatal(fmt.Sprint("pqLen did not generate expected output. Expecting 1 got", length))
	}

	// pop the message
	trigger, err = blocks["pqPop"].GetInput(0)
	if err != nil {
		t.Fatal(err)
	}
	trigger.C <- true
	if <-out != "foo" {
		t.Fatal("pqPop did not generate expected output")
	}
	if <-priorityChan != p {
		t.Fatal("pqPop did not recive the expected priority")
	}

	// check the length of the queue again to make sure pop did its thing
	lenTrigger.C <- true
	if <-out != 0.0 {
		t.Fatal("pqPop did not remove the message")
	}

}
<|endoftext|>


<commit_before>package controllers

import (
	"bufio"
	"log"
	"os"
	"reflect"
	"strings"
	"sync"
	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm"
	. "github.com/eaciit/powerplant/sec/consoleapp/models"
	tk "github.com/eaciit/toolkit"
)

var (
	wd = func() string {
		d, _ := os.Getwd()
		return d + "/"
	}()

	// mu                 = &sync.Mutex{}
	retry              = 10
	worker             = 100
	maxDataEachProcess = 500000
)

type IBaseController interface {
	// not implemented anything yet
}

type BaseController struct {
	base     IBaseController
	MongoCtx *orm.DataContext
	SqlCtx   *orm.DataContext
}

/*func (b *BaseController) ConvertMGOToSQLServer(m orm.IModel) error {
	tStart := time.Now()

	runtime.GOMAXPROCS(runtime.NumCPU())

	tk.Printf("\nConvertMGOToSQLServer: Converting %v \n", m.TableName())
	tk.Println("ConvertMGOToSQLServer: Starting to convert...\n")
	csr, e := b.MongoCtx.Connection.NewQuery().From(m.TableName()).Cursor(nil)
	if e != nil {
		return e
	}
	result := []tk.M{}
	e = csr.Fetch(&result, 0, false)
	defer csr.Close()
	if e != nil {
		return e
	}

	for idx, i := range result {
		valueType := reflect.TypeOf(m).Elem()
		for f := 0; f < valueType.NumField(); f++ {
			field := valueType.Field(f)
			bsonField := field.Tag.Get("bson")
			jsonField := field.Tag.Get("json")
			if jsonField != bsonField && field.Name != "RWMutex" && field.Name != "ModelBase" {
				i.Set(field.Name, GetMgoValue(i, bsonField))
			}
			if field.Type.Name() == "Time" {
				if i.Get(bsonField) == nil {
					i.Set(field.Name, time.Time{})
				} else {
					i.Set(field.Name, GetMgoValue(i, bsonField).(time.Time).UTC())
				}
			}
		}
		e := tk.Serde(i, m, "json")
		if e != nil {
			tk.Printf("\n------------------------- \n %#v \n\n", i)
			tk.Printf("%#v \n-------------------------  \n", m)
			tk.Printf("Completed in %v \n", time.Since(tStart))
			return e
		}

		for index := 0; index < retry; index++ {
			e = b.SqlCtx.Insert(m)
			if e == nil {
				break
			} else {
				tk.Println("retry : ", index+1)
				b.MongoCtx.Connection.Connect()
				b.SqlCtx.Connection.Connect()
			}
		}

		if e != nil {
			tk.Printf("\n------------------------- \n %#v \n\n", i)
			tk.Printf("%#v \n-------------------------  \n", m)
			tk.Printf("Completed With Error in %v \n", time.Since(tStart))
			return e
		}

		if idx%100 == 0 && idx != 0 {
			tk.Println("Completion : ", idx, "/", len(result))
		}

	}
	tk.Println("\nConvertMGOToSQLServer: Finish.")
	tk.Printf("Completed Success in %v \n", time.Since(tStart))
	return nil
}*/

func (b *BaseController) ConvertMGOToSQLServer(m orm.IModel) error {
	tStart := time.Now()

	tk.Printf("\nConvertMGOToSQLServer: Converting %v \n", m.TableName())
	tk.Println("ConvertMGOToSQLServer: Starting to convert...\n")

	c, e := b.MongoCtx.Connection.NewQuery().From(m.TableName()).Cursor(nil)

	if e != nil {
		return e
	}

	defer c.Close()

	totalData := c.Count()
	processIter := tk.ToInt(tk.ToFloat64(totalData/maxDataEachProcess, 0, tk.RoundingUp), tk.RoundingUp)

	if maxDataEachProcess == 0 {
		processIter = 0
	}

	for iter := 0; iter < processIter+1; iter++ {

		skip := iter * maxDataEachProcess
		take := maxDataEachProcess

		if maxDataEachProcess == 0 {
			take = totalData
		} else if iter == processIter {
			take = totalData - skip
		}

		csr, e := b.MongoCtx.Connection.NewQuery().From(m.TableName()).Skip(skip).Take(take).Cursor(nil)

		if e != nil {
			return e
		}

		result := []tk.M{}
		e = csr.Fetch(&result, 0, false)
		csr.Close()

		if e != nil {
			return e
		}

		dtLen := len(result)

		resPart := make([][]tk.M, worker)

		if dtLen < worker {
			resPart = make([][]tk.M, 1)
			resPart[0] = result
		} else {
			workerTaskCount := tk.ToInt(tk.ToFloat64(dtLen/worker, 0, tk.RoundingAuto), tk.RoundingAuto)
			count := 0

			for i := 0; i < worker; i++ {
				if i == worker-1 {
					resPart[i] = result[count:]
				} else {
					resPart[i] = result[count : count+workerTaskCount]
				}
				count += workerTaskCount
			}
		}

		wg := &sync.WaitGroup{}
		wg.Add(len(resPart))

		for _, val := range resPart {
			go b.Insert(val, m, wg)
		}

		wg.Wait()
	}

	tk.Println("\nConvertMGOToSQLServer: Finish.")
	tk.Printf("Completed Success in %v \n", time.Since(tStart))
	return nil
}

func (b *BaseController) Insert(result []tk.M, m orm.IModel, wg *sync.WaitGroup) {
	muinsert := &sync.Mutex{}
	for _, i := range result {
		valueType := reflect.TypeOf(m).Elem()
		for f := 0; f < valueType.NumField(); f++ {
			field := valueType.Field(f)
			bsonField := field.Tag.Get("bson")
			jsonField := field.Tag.Get("json")
			if jsonField != bsonField && field.Name != "RWMutex" && field.Name != "ModelBase" {
				i.Set(field.Name, GetMgoValue(i, bsonField))
			}
			if field.Type.Name() == "Time" {
				if i.Get(bsonField) == nil {
					i.Set(field.Name, time.Time{})
				} else {
					i.Set(field.Name, GetMgoValue(i, bsonField).(time.Time).UTC())
				}
			}
		}

		newPointer := getNewPointer(m)

		e := tk.Serde(i, newPointer, "json")

		muinsert.Lock()
		for index := 0; index < retry; index++ {
			e = b.SqlCtx.Insert(newPointer)
			if e == nil {
				break
			} else {
				// log.Printf("%T %+v", e, e)
				// tk.Println("retry : ", index+1)
				b.SqlCtx.Connection.Connect()
			}
		}
		muinsert.Unlock()

		if e != nil {
			tk.Printf("\n----------- ERROR -------------- \n %v \n %#v \n\n %#v \n-------------------------  \n", e.Error(), i, newPointer)
		}

	}
	wg.Done()
}
func GetMgoValue(d tk.M, fieldName string) interface{} {
	index := strings.Index(fieldName, ".")
	if index < 0 {
		return d.Get(fieldName)
	} else {
		return GetMgoValue(d.Get(fieldName[0:index]).(tk.M), fieldName[(index+1):len(fieldName)])
	}
}
func (b *BaseController) GetById(m orm.IModel, id interface{}, column_name ...string) error {
	var e error
	c := b.SqlCtx.Connection
	column_id := "Id"
	if column_name != nil && len(column_name) > 0 {
		column_id = column_name[0]
	}
	csr, e := c.NewQuery().From(m.(orm.IModel).TableName()).Where(dbox.Eq(column_id, id)).Cursor(nil)
	defer csr.Close()
	if e != nil {
		return e
	}
	e = csr.Fetch(m, 1, false)
	if e != nil {
		return e
	}

	return nil
}

func getNewPointer(m orm.IModel) orm.IModel {
	switch m.TableName() {
	case "PlannedMaintenance":
		return new(PlannedMaintenance)
	case "SummaryData":
		return new(SummaryData)
	case "DataBrowser":
		return new(DataBrowser)
	default:
		return m
	}

}

func (b *BaseController) Delete(m orm.IModel, id interface{}, column_name ...string) error {
	column_id := "Id"
	if column_name != nil && len(column_name) > 0 {
		column_id = column_name[0]
	}
	e := b.SqlCtx.Connection.NewQuery().From(m.(orm.IModel).TableName()).Where(dbox.Eq(column_id, id)).Delete().Exec(nil)
	if e != nil {
		return e
	}
	return nil
}

func (b *BaseController) Update(m orm.IModel, id interface{}, column_name ...string) error {
	column_id := "Id"
	if column_name != nil && len(column_name) > 0 {
		column_id = column_name[0]
	}
	e := b.SqlCtx.Connection.NewQuery().From(m.(orm.IModel).TableName()).Where(dbox.Eq(column_id, id)).Update().Exec(tk.M{"data": m})
	if e != nil {
		return e
	}
	return nil
}

func (b *BaseController) Truncate(m orm.IModel) error {
	c := b.SqlCtx.Connection
	e := c.NewQuery().From(m.(orm.IModel).TableName()).Delete().Exec(nil)
	if e != nil {
		return e
	}

	return nil
}
func (b *BaseController) CloseDb() {
	if b.MongoCtx != nil {
		b.MongoCtx.Close()
	}
	if b.SqlCtx != nil {
		b.SqlCtx.Close()
	}
}

func (b *BaseController) WriteLog(msg interface{}) {
	log.Printf("%#v\n\r", msg)
	return
}

func PrepareConnection(ConnectionType string) (dbox.IConnection, error) {
	config := ReadConfig()
	tk.Println(config["host"])
	ci := &dbox.ConnectionInfo{config["host_"+ConnectionType], config["database_"+ConnectionType], config["username_"+ConnectionType], config["password_"+ConnectionType], nil}
	c, e := dbox.NewConnection(ConnectionType, ci)

	if e != nil {
		return nil, e
	}

	e = c.Connect()
	if e != nil {
		return nil, e
	}

	return c, nil
}

func ReadConfig() map[string]string {
	ret := make(map[string]string)
	file, err := os.Open(wd + "conf/app.conf")
	if err == nil {
		defer file.Close()

		reader := bufio.NewReader(file)
		for {
			line, _, e := reader.ReadLine()
			if e != nil {
				break
			}

			sval := strings.Split(string(line), "=")
			ret[sval[0]] = sval[1]
		}
	} else {
		tk.Println(err.Error())
	}

	return ret
}
<commit_msg>Change on base<commit_after>package controllers

import (
	"bufio"
	"github.com/eaciit/dbox"
	"github.com/eaciit/orm"
	. "github.com/eaciit/powerplant/sec/consoleapp/models"
	tk "github.com/eaciit/toolkit"
	"log"
	"math"
	"os"
	"reflect"
	"strings"
	"sync"
	"time"
)

var (
	wd = func() string {
		d, _ := os.Getwd()
		return d + "/"
	}()

	// mu                 = &sync.Mutex{}
	retry              = 10
	worker             = 100
	maxDataEachProcess = 500000
)

type IBaseController interface {
	// not implemented anything yet
}

type BaseController struct {
	base     IBaseController
	MongoCtx *orm.DataContext
	SqlCtx   *orm.DataContext
}

/*func (b *BaseController) ConvertMGOToSQLServer(m orm.IModel) error {
	tStart := time.Now()

	runtime.GOMAXPROCS(runtime.NumCPU())

	tk.Printf("\nConvertMGOToSQLServer: Converting %v \n", m.TableName())
	tk.Println("ConvertMGOToSQLServer: Starting to convert...\n")
	csr, e := b.MongoCtx.Connection.NewQuery().From(m.TableName()).Cursor(nil)
	if e != nil {
		return e
	}
	result := []tk.M{}
	e = csr.Fetch(&result, 0, false)
	defer csr.Close()
	if e != nil {
		return e
	}

	for idx, i := range result {
		valueType := reflect.TypeOf(m).Elem()
		for f := 0; f < valueType.NumField(); f++ {
			field := valueType.Field(f)
			bsonField := field.Tag.Get("bson")
			jsonField := field.Tag.Get("json")
			if jsonField != bsonField && field.Name != "RWMutex" && field.Name != "ModelBase" {
				i.Set(field.Name, GetMgoValue(i, bsonField))
			}
			if field.Type.Name() == "Time" {
				if i.Get(bsonField) == nil {
					i.Set(field.Name, time.Time{})
				} else {
					i.Set(field.Name, GetMgoValue(i, bsonField).(time.Time).UTC())
				}
			}
		}
		e := tk.Serde(i, m, "json")
		if e != nil {
			tk.Printf("\n------------------------- \n %#v \n\n", i)
			tk.Printf("%#v \n-------------------------  \n", m)
			tk.Printf("Completed in %v \n", time.Since(tStart))
			return e
		}

		for index := 0; index < retry; index++ {
			e = b.SqlCtx.Insert(m)
			if e == nil {
				break
			} else {
				tk.Println("retry : ", index+1)
				b.MongoCtx.Connection.Connect()
				b.SqlCtx.Connection.Connect()
			}
		}

		if e != nil {
			tk.Printf("\n------------------------- \n %#v \n\n", i)
			tk.Printf("%#v \n-------------------------  \n", m)
			tk.Printf("Completed With Error in %v \n", time.Since(tStart))
			return e
		}

		if idx%100 == 0 && idx != 0 {
			tk.Println("Completion : ", idx, "/", len(result))
		}

	}
	tk.Println("\nConvertMGOToSQLServer: Finish.")
	tk.Printf("Completed Success in %v \n", time.Since(tStart))
	return nil
}*/

func (b *BaseController) ConvertMGOToSQLServer(m orm.IModel) error {
	tk.Printf("\nConvertMGOToSQLServer: Converting %v \n", m.TableName())
	tk.Println("ConvertMGOToSQLServer: Starting to convert...\n")
	date := time.Now()
	csr, e := b.MongoCtx.Connection.NewQuery().From(m.TableName()).Cursor(nil)
	defer csr.Close()
	if e != nil {
		return e
	}
	result := []tk.M{}
	e = csr.Fetch(&result, 0, false)
	if e != nil {
		return e
	}
	query := b.SqlCtx.Connection.NewQuery().SetConfig("multiexec", true).From(m.TableName()).Save()
	var wg sync.WaitGroup
	take := 20000
	diff := float64(len(result) / take)
	wgTotal := math.Ceil(diff)
	wgTotal++
	wg.Add(int(wgTotal))
	tk.Println("TOTAL : ", len(result))
	for x := 0; x < int(wgTotal); x++ {
		go func(swg *sync.WaitGroup, result []tk.M, skip int, max int) {
			t := skip + take
			if (skip + take) > max {
				t = max
			}
			for _, i := range result[skip:t] {
				valueType := reflect.TypeOf(m).Elem()
				for f := 0; f < valueType.NumField(); f++ {
					field := valueType.Field(f)
					bsonField := field.Tag.Get("bson")
					jsonField := field.Tag.Get("json")
					if jsonField != bsonField && field.Name != "RWMutex" && field.Name != "ModelBase" {
						i.Set(field.Name, GetMgoValue(i, bsonField))
					}
					switch field.Type.Name() {
					case "string":
						if GetMgoValue(i, bsonField) == nil {
							i.Set(field.Name, "")
						}
						break
					case "Time":
						if GetMgoValue(i, bsonField) == nil {
							i.Set(field.Name, time.Time{})
						} else {
							i.Set(field.Name, GetMgoValue(i, bsonField).(time.Time).UTC())
						}
						break
					default:
						break
					}
				}
				d := getNewPointer(m)
				e := tk.Serde(i, &d, "json")

				// query := b.SqlCtx.Connection.NewQuery().From(m.TableName()).Save()
				e = query.Exec(tk.M{"data": d})
				if e != nil {
					tk.Println(m)
					tk.Println("-------------------------------------")
					tk.Println(d)
					tk.Println("-------------------------------------")
					tk.Println(e)
					wg.Done()
				}
			}
			swg.Done()
		}(&wg, result, (x * take), len(result))
	}
	wg.Wait()

	tk.Println("\nConvertMGOToSQLServer: Finish.")
	tk.Println(time.Since(date))
	return nil
}

// mas faisal
// func (b *BaseController) ConvertMGOToSQLServer(m orm.IModel) error {
// 	tStart := time.Now()

// 	tk.Printf("\nConvertMGOToSQLServer: Converting %v \n", m.TableName())
// 	tk.Println("ConvertMGOToSQLServer: Starting to convert...\n")

// 	c, e := b.MongoCtx.Connection.NewQuery().From(m.TableName()).Cursor(nil)

// 	if e != nil {
// 		return e
// 	}

// 	defer c.Close()

// 	totalData := c.Count()
// 	processIter := tk.ToInt(tk.ToFloat64(totalData/maxDataEachProcess, 0, tk.RoundingUp), tk.RoundingUp)

// 	if maxDataEachProcess == 0 {
// 		processIter = 0
// 	}

// 	for iter := 0; iter < processIter+1; iter++ {

// 		skip := iter * maxDataEachProcess
// 		take := maxDataEachProcess

// 		if maxDataEachProcess == 0 {
// 			take = totalData
// 		} else if iter == processIter {
// 			take = totalData - skip
// 		}

// 		csr, e := b.MongoCtx.Connection.NewQuery().From(m.TableName()).Skip(skip).Take(take).Cursor(nil)

// 		if e != nil {
// 			return e
// 		}

// 		result := []tk.M{}
// 		e = csr.Fetch(&result, 0, false)
// 		csr.Close()

// 		if e != nil {
// 			return e
// 		}

// 		dtLen := len(result)

// 		resPart := make([][]tk.M, worker)

// 		if dtLen < worker {
// 			resPart = make([][]tk.M, 1)
// 			resPart[0] = result
// 		} else {
// 			workerTaskCount := tk.ToInt(tk.ToFloat64(dtLen/worker, 0, tk.RoundingAuto), tk.RoundingAuto)
// 			count := 0

// 			for i := 0; i < worker; i++ {
// 				if i == worker-1 {
// 					resPart[i] = result[count:]
// 				} else {
// 					resPart[i] = result[count : count+workerTaskCount]
// 				}
// 				count += workerTaskCount
// 			}
// 		}

// 		wg := &sync.WaitGroup{}
// 		wg.Add(len(resPart))

// 		for _, val := range resPart {
// 			go b.Insert(val, m, wg)
// 		}

// 		wg.Wait()
// 	}

// 	tk.Println("\nConvertMGOToSQLServer: Finish.")
// 	tk.Printf("Completed Success in %v \n", time.Since(tStart))
// 	return nil
// }

func (b *BaseController) Insert(result []tk.M, m orm.IModel, wg *sync.WaitGroup) {
	muinsert := &sync.Mutex{}
	for _, i := range result {
		valueType := reflect.TypeOf(m).Elem()
		for f := 0; f < valueType.NumField(); f++ {
			field := valueType.Field(f)
			bsonField := field.Tag.Get("bson")
			jsonField := field.Tag.Get("json")
			if jsonField != bsonField && field.Name != "RWMutex" && field.Name != "ModelBase" {
				i.Set(field.Name, GetMgoValue(i, bsonField))
			}
			if field.Type.Name() == "Time" {
				if i.Get(bsonField) == nil {
					i.Set(field.Name, time.Time{})
				} else {
					i.Set(field.Name, GetMgoValue(i, bsonField).(time.Time).UTC())
				}
			}
		}

		newPointer := getNewPointer(m)

		e := tk.Serde(i, newPointer, "json")

		muinsert.Lock()
		for index := 0; index < retry; index++ {
			e = b.SqlCtx.Insert(newPointer)
			if e == nil {
				wg.Done()
				break
			} else {
				// log.Printf("%T %+v", e, e)
				// tk.Println("retry : ", index+1)
				b.SqlCtx.Connection.Connect()
			}
		}
		muinsert.Unlock()

		if e != nil {
			tk.Printf("\n----------- ERROR -------------- \n %v \n %#v \n\n %#v \n-------------------------  \n", e.Error(), i, newPointer)
			wg.Done()
		}

	}
	wg.Done()
}
func GetMgoValue(d tk.M, fieldName string) interface{} {
	index := strings.Index(fieldName, ".")
	if index < 0 {
		return d.Get(fieldName)
	} else {
		data := d.Get(fieldName[0:index])
		if data != nil {
			return GetMgoValue(data.(tk.M), fieldName[(index+1):len(fieldName)])
		} else {
			return nil
		}
	}
}
func (b *BaseController) GetById(m orm.IModel, id interface{}, column_name ...string) error {
	var e error
	c := b.SqlCtx.Connection
	column_id := "Id"
	if column_name != nil && len(column_name) > 0 {
		column_id = column_name[0]
	}
	csr, e := c.NewQuery().From(m.(orm.IModel).TableName()).Where(dbox.Eq(column_id, id)).Cursor(nil)
	defer csr.Close()
	if e != nil {
		return e
	}
	e = csr.Fetch(m, 1, false)
	if e != nil {
		return e
	}

	return nil
}

func getNewPointer(m orm.IModel) orm.IModel {
	switch m.TableName() {
	case "MaintenanceCostByHour":
		return new(MaintenanceCostByHour)
	case "MaintenanceCost":
		return new(MaintenanceCost)
	case "PlannedMaintenance":
		return new(PlannedMaintenance)
	case "SummaryData":
		return new(SummaryData)
	case "DataBrowser":
		return new(DataBrowser)
	default:
		return m
	}

}

func (b *BaseController) Delete(m orm.IModel, id interface{}, column_name ...string) error {
	column_id := "Id"
	if column_name != nil && len(column_name) > 0 {
		column_id = column_name[0]
	}
	e := b.SqlCtx.Connection.NewQuery().From(m.(orm.IModel).TableName()).Where(dbox.Eq(column_id, id)).Delete().Exec(nil)
	if e != nil {
		return e
	}
	return nil
}

func (b *BaseController) Update(m orm.IModel, id interface{}, column_name ...string) error {
	column_id := "Id"
	if column_name != nil && len(column_name) > 0 {
		column_id = column_name[0]
	}
	e := b.SqlCtx.Connection.NewQuery().From(m.(orm.IModel).TableName()).Where(dbox.Eq(column_id, id)).Update().Exec(tk.M{"data": m})
	if e != nil {
		return e
	}
	return nil
}

func (b *BaseController) Truncate(m orm.IModel) error {
	c := b.SqlCtx.Connection
	e := c.NewQuery().From(m.(orm.IModel).TableName()).Delete().Exec(nil)
	if e != nil {
		return e
	}

	return nil
}
func (b *BaseController) CloseDb() {
	if b.MongoCtx != nil {
		b.MongoCtx.Close()
	}
	if b.SqlCtx != nil {
		b.SqlCtx.Close()
	}
}

func (b *BaseController) WriteLog(msg interface{}) {
	log.Printf("%#v\n\r", msg)
	return
}

func PrepareConnection(ConnectionType string) (dbox.IConnection, error) {
	config := ReadConfig()
	tk.Println(config["host"])
	ci := &dbox.ConnectionInfo{config["host_"+ConnectionType], config["database_"+ConnectionType], config["username_"+ConnectionType], config["password_"+ConnectionType], nil}
	c, e := dbox.NewConnection(ConnectionType, ci)

	if e != nil {
		return nil, e
	}

	e = c.Connect()
	if e != nil {
		return nil, e
	}

	return c, nil
}

func ReadConfig() map[string]string {
	ret := make(map[string]string)
	file, err := os.Open(wd + "conf/app.conf")
	if err == nil {
		defer file.Close()

		reader := bufio.NewReader(file)
		for {
			line, _, e := reader.ReadLine()
			if e != nil {
				break
			}

			sval := strings.Split(string(line), "=")
			ret[sval[0]] = sval[1]
		}
	} else {
		tk.Println(err.Error())
	}

	return ret
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"flag"
	"fmt"
	"mig"
	"mig/client"
	"mig/modules"
	"os"
	"os/signal"
	"time"
)

// build version
var version string

func usage() {
	fmt.Printf(`%s - Mozilla InvestiGator command line client
usage: %s <module> <global options> <module parameters>

--- Global options ---

-c <path>	path to an alternative config file. If not set, use ~/.migrc
-e <duration>	time after which the action expires. 60 seconds by default.
		example: -e 300s (5 minutes)
-i <file>	load and run action from a file. supersedes other action flags.
-show <mode>	type of results to show. if not set, default is 'found'.
		* found: 	only print positive results
		* notfound: 	only print negative results
		* all: 		print all results
-render <mode>	defines how results should be rendered:
		* text (default):	results are printed to the console
		* map:			results are geolocated and a google map is generated
-t <target>	target to launch the action on. The default targets all online
		agents (idle and offline agents are ignored).
		examples:
		* linux agents:          -t "queueloc LIKE 'linux.%%'"
		* agents named *mysql*:  -t "name like '%%mysql%%'"
		* proxied linux agents:  -t "queueloc LIKE 'linux.%%' AND environment->>'isproxied' = 'true'"
		* agents operated by IT: -t "tags#>>'{operator}'='IT'"

Progress information is sent to stderr, silence it with "2>/dev/null".
Results are sent to stdout, redirect them with "1>/path/to/file".

--- Modules documentation ---
Each module provides its own set of parameters. Module parameters must be set *after*
global options. Help is available by calling "<module> help". Available modules are:
`, os.Args[0], os.Args[0])
	for module, _ := range modules.Available {
		fmt.Printf("* %s\n", module)
	}
	fmt.Printf("To access a module documentation, use: %s <module> help\n", os.Args[0])
	os.Exit(1)
}

func continueOnFlagError() {
	return
}

func main() {
	var (
		conf                                           client.Configuration
		cli                                            client.Client
		err                                            error
		op                                             mig.Operation
		a                                              mig.Action
		migrc, show, render, target, expiration, afile string
		modargs                                        []string
		modRunner                                      interface{}
	)
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "%v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	fs := flag.NewFlagSet("mig flag", flag.ContinueOnError)
	fs.Usage = continueOnFlagError
	fs.StringVar(&migrc, "c", homedir+"/.migrc", "alternative configuration file")
	fs.StringVar(&show, "show", "found", "type of results to show")
	fs.StringVar(&render, "render", "text", "results rendering mode")
	fs.StringVar(&target, "t", fmt.Sprintf("status='%s' AND mode='daemon'", mig.AgtStatusOnline), "action target")
	fs.StringVar(&expiration, "e", "300s", "expiration")
	fs.StringVar(&afile, "i", "/path/to/file", "Load action from file")

	// if first argument is missing, or is help, print help
	// otherwise, pass the remainder of the arguments to the module for parsing
	// this client is agnostic to module parameters
	if len(os.Args) < 2 || os.Args[1] == "help" || os.Args[1] == "-h" || os.Args[1] == "--help" {
		usage()
	}

	if len(os.Args) < 2 || os.Args[1] == "-V" {
		fmt.Println(version)
		os.Exit(0)
	}

	// when reading the action from a file, go directly to launch
	if os.Args[1] == "-i" {
		err = fs.Parse(os.Args[1:])
		if err != nil {
			panic(err)
		}
		if afile == "/path/to/file" {
			panic("-i flag must take an action file path as argument")
		}
		a, err = mig.ActionFromFile(afile)
		if err != nil {
			panic(err)
		}
		fmt.Fprintf(os.Stderr, "[info] launching action from file, all flags are ignored\n")
		goto readytolaunch
	}

	// arguments parsing works as follow:
	// * os.Args[1] must contain the name of the module to launch. we first verify
	//   that a module exist for this name and then continue parsing
	// * os.Args[2:] contains both global options and module parameters. We parse the
	//   whole []string to extract global options, and module parameters will be left
	//   unparsed in fs.Args()
	// * fs.Args() with the module parameters is passed as a string to the module parser
	//   which will return a module operation to store in the action
	op.Module = os.Args[1]
	if _, ok := modules.Available[op.Module]; !ok {
		panic("Unknown module " + op.Module)
	}

	// -- Ugly hack Warning --
	// Parse() will fail on the first flag that is not defined, but in our case module flags
	// are defined in the module packages and not in this program. Therefore, the flag parse error
	// is expected. Unfortunately, Parse() writes directly to stderr and displays the error to
	// the user, which confuses them. The right fix would be to prevent Parse() from writing to
	// stderr, since that's really the job of the calling program, but in the meantime we work around
	// it by redirecting stderr to null before calling Parse(), and put it back to normal afterward.
	// for ref, issue is at https://github.com/golang/go/blob/master/src/flag/flag.go#L793
	fs.SetOutput(os.NewFile(uintptr(87592), os.DevNull))
	err = fs.Parse(os.Args[2:])
	fs.SetOutput(nil)
	if err != nil {
		// ignore the flag not defined error, which is expected because
		// module parameters are defined in modules and not in main
		if len(err.Error()) > 30 && err.Error()[0:29] == "flag provided but not defined" {
			// requeue the parameter that failed
			modargs = append(modargs, err.Error()[31:])
		} else {
			// if it's another error, panic
			panic(err)
		}
	}
	for _, arg := range fs.Args() {
		modargs = append(modargs, arg)
	}
	modRunner = modules.Available[op.Module].Runner()
	if _, ok := modRunner.(modules.HasParamsParser); !ok {
		fmt.Fprintf(os.Stderr, "[error] module '%s' does not support command line invocation\n", op.Module)
		os.Exit(2)
	}
	op.Parameters, err = modRunner.(modules.HasParamsParser).ParamsParser(modargs)
	if err != nil || op.Parameters == nil {
		panic(err)
	}
	a.Operations = append(a.Operations, op)

	for _, arg := range os.Args[1:] {
		a.Name += arg + " "
	}
	a.Target = target

readytolaunch:
	// instanciate an API client
	conf, err = client.ReadConfiguration(migrc)
	if err != nil {
		panic(err)
	}
	cli, err = client.NewClient(conf, "cmd-"+version)
	if err != nil {
		panic(err)
	}

	// set the validity 60 second in the past to deal with clock skew
	a.ValidFrom = time.Now().Add(-60 * time.Second).UTC()
	period, err := time.ParseDuration(expiration)
	if err != nil {
		panic(err)
	}
	a.ExpireAfter = a.ValidFrom.Add(period)
	// add extra 60 seconds taken for clock skew
	a.ExpireAfter = a.ExpireAfter.Add(60 * time.Second).UTC()

	asig, err := cli.SignAction(a)
	if err != nil {
		panic(err)
	}
	a = asig

	// evaluate target before launch, give a change to cancel before going out to agents
	agents, err := cli.EvaluateAgentTarget(a.Target)
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(os.Stderr, "\x1b[33m%d agents will be targeted. ctrl+c to cancel. launching in ", len(agents))
	for i := 5; i > 0; i-- {
		time.Sleep(1 * time.Second)
		fmt.Fprintf(os.Stderr, "%d ", i)
	}
	fmt.Fprintf(os.Stderr, "GO\n\x1b[0m")

	// launch and follow
	a, err = cli.PostAction(a)
	if err != nil {
		panic(err)
	}
	c := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		err = cli.FollowAction(a)
		if err != nil {
			panic(err)
		}
		done <- true
	}()
	select {
	case <-c:
		fmt.Fprintf(os.Stderr, "stop following action. agents may still be running. printing available results:\n")
		goto printresults
	case <-done:
		goto printresults
	}
printresults:
	err = cli.PrintActionResults(a, show, render)
	if err != nil {
		panic(err)
	}
}
<commit_msg>[minor] fix issue where terminal foreground would not reset on SIGINT<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"flag"
	"fmt"
	"mig"
	"mig/client"
	"mig/modules"
	"os"
	"os/signal"
	"time"
)

// build version
var version string

func usage() {
	fmt.Printf(`%s - Mozilla InvestiGator command line client
usage: %s <module> <global options> <module parameters>

--- Global options ---

-c <path>	path to an alternative config file. If not set, use ~/.migrc
-e <duration>	time after which the action expires. 60 seconds by default.
		example: -e 300s (5 minutes)
-i <file>	load and run action from a file. supersedes other action flags.
-show <mode>	type of results to show. if not set, default is 'found'.
		* found: 	only print positive results
		* notfound: 	only print negative results
		* all: 		print all results
-render <mode>	defines how results should be rendered:
		* text (default):	results are printed to the console
		* map:			results are geolocated and a google map is generated
-t <target>	target to launch the action on. The default targets all online
		agents (idle and offline agents are ignored).
		examples:
		* linux agents:          -t "queueloc LIKE 'linux.%%'"
		* agents named *mysql*:  -t "name like '%%mysql%%'"
		* proxied linux agents:  -t "queueloc LIKE 'linux.%%' AND environment->>'isproxied' = 'true'"
		* agents operated by IT: -t "tags#>>'{operator}'='IT'"

Progress information is sent to stderr, silence it with "2>/dev/null".
Results are sent to stdout, redirect them with "1>/path/to/file".

--- Modules documentation ---
Each module provides its own set of parameters. Module parameters must be set *after*
global options. Help is available by calling "<module> help". Available modules are:
`, os.Args[0], os.Args[0])
	for module, _ := range modules.Available {
		fmt.Printf("* %s\n", module)
	}
	fmt.Printf("To access a module documentation, use: %s <module> help\n", os.Args[0])
	os.Exit(1)
}

func continueOnFlagError() {
	return
}

func main() {
	var (
		conf                                           client.Configuration
		cli                                            client.Client
		err                                            error
		op                                             mig.Operation
		a                                              mig.Action
		migrc, show, render, target, expiration, afile string
		modargs                                        []string
		modRunner                                      interface{}
	)
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "%v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	fs := flag.NewFlagSet("mig flag", flag.ContinueOnError)
	fs.Usage = continueOnFlagError
	fs.StringVar(&migrc, "c", homedir+"/.migrc", "alternative configuration file")
	fs.StringVar(&show, "show", "found", "type of results to show")
	fs.StringVar(&render, "render", "text", "results rendering mode")
	fs.StringVar(&target, "t", fmt.Sprintf("status='%s' AND mode='daemon'", mig.AgtStatusOnline), "action target")
	fs.StringVar(&expiration, "e", "300s", "expiration")
	fs.StringVar(&afile, "i", "/path/to/file", "Load action from file")

	// if first argument is missing, or is help, print help
	// otherwise, pass the remainder of the arguments to the module for parsing
	// this client is agnostic to module parameters
	if len(os.Args) < 2 || os.Args[1] == "help" || os.Args[1] == "-h" || os.Args[1] == "--help" {
		usage()
	}

	if len(os.Args) < 2 || os.Args[1] == "-V" {
		fmt.Println(version)
		os.Exit(0)
	}

	// when reading the action from a file, go directly to launch
	if os.Args[1] == "-i" {
		err = fs.Parse(os.Args[1:])
		if err != nil {
			panic(err)
		}
		if afile == "/path/to/file" {
			panic("-i flag must take an action file path as argument")
		}
		a, err = mig.ActionFromFile(afile)
		if err != nil {
			panic(err)
		}
		fmt.Fprintf(os.Stderr, "[info] launching action from file, all flags are ignored\n")
		goto readytolaunch
	}

	// arguments parsing works as follow:
	// * os.Args[1] must contain the name of the module to launch. we first verify
	//   that a module exist for this name and then continue parsing
	// * os.Args[2:] contains both global options and module parameters. We parse the
	//   whole []string to extract global options, and module parameters will be left
	//   unparsed in fs.Args()
	// * fs.Args() with the module parameters is passed as a string to the module parser
	//   which will return a module operation to store in the action
	op.Module = os.Args[1]
	if _, ok := modules.Available[op.Module]; !ok {
		panic("Unknown module " + op.Module)
	}

	// -- Ugly hack Warning --
	// Parse() will fail on the first flag that is not defined, but in our case module flags
	// are defined in the module packages and not in this program. Therefore, the flag parse error
	// is expected. Unfortunately, Parse() writes directly to stderr and displays the error to
	// the user, which confuses them. The right fix would be to prevent Parse() from writing to
	// stderr, since that's really the job of the calling program, but in the meantime we work around
	// it by redirecting stderr to null before calling Parse(), and put it back to normal afterward.
	// for ref, issue is at https://github.com/golang/go/blob/master/src/flag/flag.go#L793
	fs.SetOutput(os.NewFile(uintptr(87592), os.DevNull))
	err = fs.Parse(os.Args[2:])
	fs.SetOutput(nil)
	if err != nil {
		// ignore the flag not defined error, which is expected because
		// module parameters are defined in modules and not in main
		if len(err.Error()) > 30 && err.Error()[0:29] == "flag provided but not defined" {
			// requeue the parameter that failed
			modargs = append(modargs, err.Error()[31:])
		} else {
			// if it's another error, panic
			panic(err)
		}
	}
	for _, arg := range fs.Args() {
		modargs = append(modargs, arg)
	}
	modRunner = modules.Available[op.Module].Runner()
	if _, ok := modRunner.(modules.HasParamsParser); !ok {
		fmt.Fprintf(os.Stderr, "[error] module '%s' does not support command line invocation\n", op.Module)
		os.Exit(2)
	}
	op.Parameters, err = modRunner.(modules.HasParamsParser).ParamsParser(modargs)
	if err != nil || op.Parameters == nil {
		panic(err)
	}
	a.Operations = append(a.Operations, op)

	for _, arg := range os.Args[1:] {
		a.Name += arg + " "
	}
	a.Target = target

readytolaunch:
	// instanciate an API client
	conf, err = client.ReadConfiguration(migrc)
	if err != nil {
		panic(err)
	}
	cli, err = client.NewClient(conf, "cmd-"+version)
	if err != nil {
		panic(err)
	}

	// set the validity 60 second in the past to deal with clock skew
	a.ValidFrom = time.Now().Add(-60 * time.Second).UTC()
	period, err := time.ParseDuration(expiration)
	if err != nil {
		panic(err)
	}
	a.ExpireAfter = a.ValidFrom.Add(period)
	// add extra 60 seconds taken for clock skew
	a.ExpireAfter = a.ExpireAfter.Add(60 * time.Second).UTC()

	asig, err := cli.SignAction(a)
	if err != nil {
		panic(err)
	}
	a = asig

	// evaluate target before launch, give a change to cancel before going out to agents
	agents, err := cli.EvaluateAgentTarget(a.Target)
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(os.Stderr, "\x1b[33m%d agents will be targeted. ctrl+c to cancel. launching in \x1b[0m", len(agents))
	for i := 5; i > 0; i-- {
		time.Sleep(1 * time.Second)
		fmt.Fprintf(os.Stderr, "\x1b[33m%d\x1b[0m ", i)
	}
	fmt.Fprintf(os.Stderr, "\x1b[33mGO\n\x1b[0m")

	// launch and follow
	a, err = cli.PostAction(a)
	if err != nil {
		panic(err)
	}
	c := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		err = cli.FollowAction(a)
		if err != nil {
			panic(err)
		}
		done <- true
	}()
	select {
	case <-c:
		fmt.Fprintf(os.Stderr, "stop following action. agents may still be running. printing available results:\n")
		goto printresults
	case <-done:
		goto printresults
	}
printresults:
	err = cli.PrintActionResults(a, show, render)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>/*
Package godic provides a simple utility to auto-generate idiomatic dependency
injection containers that can benefit from Go's type checking system.
*/
package godic

import (
	"os"
  "text/template"
  "unicode"
)

// Generator defines what a dependency injection container's code is going to
// look like. The properties of an instance of Generator directly map to
// source code entities in the produced code.
type Generator struct {

  // Package specifies which package the container is going
  // to belong to.
	Package string

  // Imports is the list of imports the generated code will need.
	Imports []string

  // Type is the Go type of the generated container.
	Type string

  // Deps is the list of dependency definitions that will make up the
  // lookup/create methods and the properties of the container.
	Deps []Dep
}

// Dep is the definition for a dependency
type Dep struct {
  // Type is the Go type of the object to be created when the lookup/create
  // function is invoked
  Type,

  // Name is used in constructing the name of the struct field, lookup and
  // create function. As an example, if the Name is "dependency", the struct
  // field name will be "dependency", the create function will be named
  // "NewDependency" and the lookup function will be named just like a getter,
  // i.e. "Dependency" (note that lookup/create are public, but not the field)
  Name,

  // Func is the text for the Go code that will be the body of the builder
  // function. Providing only the body of the function only avoids repetition;
  // the lookup function always has the same structure, and the signatures
  // for both the lookup and create functions are similar and easy to generate
  // automatically
  Func string
}

// Generate renders the code template for
func (c *Generator) Generate() {
	t, err := template.
		New("generate").
		Funcs(template.FuncMap{
			"ucfirst": ucfirst,
			"lcfirst": lcfirst,
		}).
		Parse(tplstr)

  if err != nil {
  	panic(err)
  }

  err = t.Execute(os.Stdout, c)

  if err != nil {
  	panic(err)
  }
}

var tplstr = `package {{ .Package }}

{{ range .Imports }}import "{{.}}"
{{ end }}

type {{ .Type }} struct {
{{ range .Deps }} {{ .Name | lcfirst }} {{ .Type }}
{{ end }}{{ "}" }}

{{ $ctype := .Type }}
{{ range .Deps }}func (c *{{ $ctype }}) New{{ .Name | ucfirst }}() {{ .Type }} {{ .Func }}

func (c *{{ $ctype }}) {{ .Name | ucfirst }}() {{ .Type }} {
  if c.{{ .Name | lcfirst }} == nil {
    c.{{ .Name | lcfirst }} = c.New{{ .Name | ucfirst }}()
  }

  return c.{{ .Name | lcfirst }}
}

{{ end }}
`

// ucfirst is an internal utility function to make the first character of a
// string upper case (i.e. an exported Go name)
func ucfirst(s string) string {
  tmp := []rune(s)
  tmp[0] = unicode.ToUpper(tmp[0])
  return string(tmp)
}

// lcfirst is an internal utility function to make the first character of a
// string lower case (i.e. a non-exported Go name)
func lcfirst(s string) string {
  tmp := []rune(s)
  tmp[0] = unicode.ToLower(tmp[0])
  return string(tmp)
}
<commit_msg>minor improvements to documentation<commit_after>/*
Package godic provides a simple utility to auto-generate idiomatic dependency
injection containers that can benefit from Go's type checking system.
*/
package godic

import (
	"os"
  "text/template"
  "unicode"
)

// Generator defines what a dependency injection container's code is going to
// look like. The properties of an instance of Generator directly map to
// source code entities in the produced code.
type Generator struct {

  // Package specifies which package the container is going
  // to belong to.
	Package string

  // Imports is the list of imports the generated code will need.
	Imports []string

  // Type is the Go type of the generated container.
	Type string

  // Deps is the list of dependency definitions that will make up the
  // lookup/create methods and the properties of the container.
	Deps []Dep
}

// Dep is the definition for a dependency.
type Dep struct {
  // Type is the Go type of the object to be created when the lookup/create
  // function is invoked.
  Type string

  // Name is used in constructing the name of the struct field, lookup and
  // create function. As an example, if the Name is "dependency", the struct
  // field name will be "dependency", the create function will be named
  // "NewDependency" and the lookup function will be named just like a getter,
  // i.e. "Dependency" (note that lookup/create are public, but the struct
  // field is private).
  Name string

  // Func is the text for the Go code that will be the body of the builder
  // function. Providing only the body of the function only avoids repetition;
  // the lookup function always has the same structure, and the signatures
  // for both the lookup and create functions are similar and easy to generate
  // automatically.
  Func string
}

// Generate renders the code template with the Generator properties to create
// the final DIC code which will be output to STDOUT.
func (c *Generator) Generate() {
	t, err := template.
		New("generate").
		Funcs(template.FuncMap{
			"ucfirst": ucfirst,
			"lcfirst": lcfirst,
		}).
		Parse(tplstr)

  if err != nil {
  	panic(err)
  }

  err = t.Execute(os.Stdout, c)

  if err != nil {
  	panic(err)
  }
}

var tplstr = `package {{ .Package }}

{{ range .Imports }}import "{{.}}"
{{ end }}

type {{ .Type }} struct {
{{ range .Deps }} {{ .Name | lcfirst }} {{ .Type }}
{{ end }}{{ "}" }}

{{ $ctype := .Type }}
{{ range .Deps }}func (c *{{ $ctype }}) New{{ .Name | ucfirst }}() {{ .Type }} {{ .Func }}

func (c *{{ $ctype }}) {{ .Name | ucfirst }}() {{ .Type }} {
  if c.{{ .Name | lcfirst }} == nil {
    c.{{ .Name | lcfirst }} = c.New{{ .Name | ucfirst }}()
  }

  return c.{{ .Name | lcfirst }}
}

{{ end }}
`

// ucfirst is an internal utility function to make the first character of a
// string upper case (i.e. an exported Go name)
func ucfirst(s string) string {
  tmp := []rune(s)
  tmp[0] = unicode.ToUpper(tmp[0])
  return string(tmp)
}

// lcfirst is an internal utility function to make the first character of a
// string lower case (i.e. a non-exported Go name)
func lcfirst(s string) string {
  tmp := []rune(s)
  tmp[0] = unicode.ToLower(tmp[0])
  return string(tmp)
}
<|endoftext|>