<commit_before>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	"code.cloudfoundry.org/guardian/guardiancmd"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/localip"
	"github.com/concourse/atc"
	"github.com/jessevdk/go-flags"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
)

type GardenBackend struct {
	guardiancmd.ServerCommand

	DNS DNSConfig `group:"DNS Proxy Configuration" namespace:"dns-proxy"`
}

func (cmd WorkerCommand) lessenRequirements(command *flags.Command) {
	command.FindOptionByLongName("garden-bind-port").Default = []string{"7777"}

	// configured as work-dir/depot
	command.FindOptionByLongName("garden-depot").Required = false

	// un-configure graph (default /var/gdn/graph)
	command.FindOptionByLongName("garden-graph").Required = false
	command.FindOptionByLongName("garden-graph").Default = []string{}

	// these are provided as assets embedded in the 'concourse' binary
	command.FindOptionByLongName("garden-runc-bin").Required = false
	command.FindOptionByLongName("garden-dadoo-bin").Required = false
	command.FindOptionByLongName("garden-init-bin").Required = false
	command.FindOptionByLongName("garden-nstar-bin").Required = false
	command.FindOptionByLongName("garden-tar-bin").Required = false

	// configured as work-dir/volumes
	command.FindOptionByLongName("baggageclaim-volumes").Required = false
}

func (cmd *WorkerCommand) gardenRunner(logger lager.Logger, hasAssets bool) (atc.Worker, ifrit.Runner, error) {
	err := cmd.checkRoot()
	if err != nil {
		return atc.Worker{}, nil, err
	}

	depotDir := filepath.Join(cmd.WorkDir.Path(), "depot")

	// must be readable by other users so unprivileged containers can run their
	// own `initc' process
	err = os.MkdirAll(depotDir, 0755)
	if err != nil {
		return atc.Worker{}, nil, err
	}

	cmd.Garden.Server.BindIP = guardiancmd.IPFlag(cmd.BindIP)
	cmd.Garden.Containers.Dir = depotDir

	cmd.Garden.Network.AllowHostAccess = true

	worker := atc.Worker{
		Platform: "linux",
		Tags:     cmd.Tags,
		Team:     cmd.TeamName,

		HTTPProxyURL:  cmd.HTTPProxy.String(),
		HTTPSProxyURL: cmd.HTTPSProxy.String(),
		NoProxy:       strings.Join(cmd.NoProxy, ","),
		StartTime:     time.Now().Unix(),
	}

	if hasAssets {
		cmd.Garden.Bin.Runc = cmd.assetPath("bin", "runc")
		cmd.Garden.Bin.Dadoo = guardiancmd.FileFlag(cmd.assetPath("bin", "dadoo"))
		cmd.Garden.Bin.Init = guardiancmd.FileFlag(cmd.assetPath("bin", "init"))
		cmd.Garden.Bin.NSTar = guardiancmd.FileFlag(cmd.assetPath("bin", "nstar"))
		cmd.Garden.Bin.Tar = guardiancmd.FileFlag(cmd.assetPath("bin", "tar"))

		iptablesDir := cmd.assetPath("iptables")
		cmd.Garden.Bin.IPTables = guardiancmd.FileFlag(filepath.Join(iptablesDir, "sbin", "iptables"))

		worker.ResourceTypes, err = cmd.extractResources(logger.Session("extract-resources"))
		if err != nil {
			return atc.Worker{}, nil, err
		}
	}

	worker.Name, err = cmd.workerName()
	if err != nil {
		return atc.Worker{}, nil, err
	}

	members := grouper.Members{
		{
			Name:   "garden-runc",
			Runner: &cmd.Garden.ServerCommand,
		},
	}

	if cmd.Garden.DNS.Enable {
		dnsProxyRunner, err := cmd.dnsProxyRunner(logger.Session("dns-proxy"))
		if err != nil {
			return atc.Worker{}, nil, err
		}

		lip, err := localip.LocalIP()
		if err != nil {
			return atc.Worker{}, nil, err
		}

		cmd.Garden.Network.AdditionalDNSServers = append(
			cmd.Garden.Network.AdditionalDNSServers,
			guardiancmd.IPFlag(net.ParseIP(lip)),
		)

		members = append(members, grouper.Member{
			Name:   "dns-proxy",
			Runner: dnsProxyRunner,
		})
	}

	return worker, grouper.NewParallel(os.Interrupt, members), nil
}

func (cmd *WorkerCommand) extractResources(logger lager.Logger) ([]atc.WorkerResourceType, error) {
	var resourceTypes []atc.WorkerResourceType

	resourcesDir := cmd.assetPath("resources")

	infos, err := ioutil.ReadDir(resourcesDir)
	if err != nil {
		logger.Error("failed-to-list-resource-assets", err)
		return nil, err
	}

	for _, info := range infos {
		resourceType := info.Name()

		workerResourceType, err := cmd.extractResource(
			logger.Session("extract", lager.Data{"resource-type": resourceType}),
			resourcesDir,
			resourceType,
		)
		if err != nil {
			logger.Error("failed-to-extract-resource", err)
			return nil, err
		}

		resourceTypes = append(resourceTypes, workerResourceType)
	}

	return resourceTypes, nil
}

func (cmd *WorkerCommand) extractResource(
	logger lager.Logger,
	resourcesDir string,
	resourceType string,
) (atc.WorkerResourceType, error) {
	resourceImagesDir := cmd.assetPath("resource-images")
	tarBin := cmd.assetPath("bin", "tar")

	archive := filepath.Join(resourcesDir, resourceType, "rootfs.tar.gz")

	extractedDir := filepath.Join(resourceImagesDir, resourceType)

	rootfsDir := filepath.Join(extractedDir, "rootfs")
	okMarker := filepath.Join(extractedDir, "ok")

	var version string
	versionFile, err := os.Open(filepath.Join(resourcesDir, resourceType, "version"))
	if err != nil {
		logger.Error("failed-to-read-version", err)
		return atc.WorkerResourceType{}, err
	}

	_, err = fmt.Fscanf(versionFile, "%s", &version)
	if err != nil {
		logger.Error("failed-to-parse-version", err)
		return atc.WorkerResourceType{}, err
	}

	defer versionFile.Close()

	var privileged bool
	_, err = os.Stat(filepath.Join(resourcesDir, resourceType, "privileged"))
	if err == nil {
		privileged = true
	}

	_, err = os.Stat(okMarker)
	if err == nil {
		logger.Info("already-extracted")
	} else {
		logger.Info("extracting")

		err := os.RemoveAll(rootfsDir)
		if err != nil {
			logger.Error("failed-to-clear-out-existing-rootfs", err)
			return atc.WorkerResourceType{}, err
		}

		err = os.MkdirAll(rootfsDir, 0755)
		if err != nil {
			logger.Error("failed-to-create-rootfs-dir", err)
			return atc.WorkerResourceType{}, err
		}

		tar := exec.Command(tarBin, "-zxf", archive, "-C", rootfsDir)

		output, err := tar.CombinedOutput()
		if err != nil {
			logger.Error("failed-to-extract-resource", err, lager.Data{
				"output": string(output),
			})
			return atc.WorkerResourceType{}, err
		}

		ok, err := os.Create(okMarker)
		if err != nil {
			logger.Error("failed-to-create-ok-marker", err)
			return atc.WorkerResourceType{}, err
		}

		err = ok.Close()
		if err != nil {
			logger.Error("failed-to-close-ok-marker", err)
			return atc.WorkerResourceType{}, err
		}
	}

	return atc.WorkerResourceType{
		Type:       resourceType,
		Image:      rootfsDir,
		Version:    version,
		Privileged: privileged,
	}, nil
}

var ErrNotRoot = errors.New("worker must be run as root")

func (cmd *WorkerCommand) checkRoot() error {
	currentUser, err := user.Current()
	if err != nil {
		return err
	}

	if currentUser.Uid != "0" {
		return ErrNotRoot
	}

	return nil
}

func (cmd *WorkerCommand) dnsProxyRunner(logger lager.Logger) (ifrit.Runner, error) {
	server, err := cmd.Garden.DNS.Server()
	if err != nil {
		return nil, err
	}

	return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
		server.NotifyStartedFunc = func() {
			close(ready)
			logger.Info("started")
		}

		serveErr := make(chan error, 1)

		go func() {
			serveErr <- server.ListenAndServe()
		}()

		for {
			select {
			case err := <-serveErr:
				return err
			case <-signals:
				server.Shutdown()
			}
		}
	}), nil
}
<commit_msg>update to new path for runc<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	"code.cloudfoundry.org/guardian/guardiancmd"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/localip"
	"github.com/concourse/atc"
	"github.com/jessevdk/go-flags"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
)

type GardenBackend struct {
	guardiancmd.ServerCommand

	DNS DNSConfig `group:"DNS Proxy Configuration" namespace:"dns-proxy"`
}

func (cmd WorkerCommand) lessenRequirements(command *flags.Command) {
	command.FindOptionByLongName("garden-bind-port").Default = []string{"7777"}

	// configured as work-dir/depot
	command.FindOptionByLongName("garden-depot").Required = false

	// un-configure graph (default /var/gdn/graph)
	command.FindOptionByLongName("garden-graph").Required = false
	command.FindOptionByLongName("garden-graph").Default = []string{}

	// these are provided as assets embedded in the 'concourse' binary
	command.FindOptionByLongName("garden-runc-bin").Required = false
	command.FindOptionByLongName("garden-dadoo-bin").Required = false
	command.FindOptionByLongName("garden-init-bin").Required = false
	command.FindOptionByLongName("garden-nstar-bin").Required = false
	command.FindOptionByLongName("garden-tar-bin").Required = false

	// configured as work-dir/volumes
	command.FindOptionByLongName("baggageclaim-volumes").Required = false
}

func (cmd *WorkerCommand) gardenRunner(logger lager.Logger, hasAssets bool) (atc.Worker, ifrit.Runner, error) {
	err := cmd.checkRoot()
	if err != nil {
		return atc.Worker{}, nil, err
	}

	depotDir := filepath.Join(cmd.WorkDir.Path(), "depot")

	// must be readable by other users so unprivileged containers can run their
	// own `initc' process
	err = os.MkdirAll(depotDir, 0755)
	if err != nil {
		return atc.Worker{}, nil, err
	}

	cmd.Garden.Server.BindIP = guardiancmd.IPFlag(cmd.BindIP)
	cmd.Garden.Containers.Dir = depotDir

	cmd.Garden.Network.AllowHostAccess = true

	worker := atc.Worker{
		Platform: "linux",
		Tags:     cmd.Tags,
		Team:     cmd.TeamName,

		HTTPProxyURL:  cmd.HTTPProxy.String(),
		HTTPSProxyURL: cmd.HTTPSProxy.String(),
		NoProxy:       strings.Join(cmd.NoProxy, ","),
		StartTime:     time.Now().Unix(),
	}

	if hasAssets {
		cmd.Garden.Runtime.Plugin = cmd.assetPath("bin", "runc")
		cmd.Garden.Bin.Dadoo = guardiancmd.FileFlag(cmd.assetPath("bin", "dadoo"))
		cmd.Garden.Bin.Init = guardiancmd.FileFlag(cmd.assetPath("bin", "init"))
		cmd.Garden.Bin.NSTar = guardiancmd.FileFlag(cmd.assetPath("bin", "nstar"))
		cmd.Garden.Bin.Tar = guardiancmd.FileFlag(cmd.assetPath("bin", "tar"))

		iptablesDir := cmd.assetPath("iptables")
		cmd.Garden.Bin.IPTables = guardiancmd.FileFlag(filepath.Join(iptablesDir, "sbin", "iptables"))

		worker.ResourceTypes, err = cmd.extractResources(logger.Session("extract-resources"))
		if err != nil {
			return atc.Worker{}, nil, err
		}
	}

	worker.Name, err = cmd.workerName()
	if err != nil {
		return atc.Worker{}, nil, err
	}

	members := grouper.Members{
		{
			Name:   "garden-runc",
			Runner: &cmd.Garden.ServerCommand,
		},
	}

	if cmd.Garden.DNS.Enable {
		dnsProxyRunner, err := cmd.dnsProxyRunner(logger.Session("dns-proxy"))
		if err != nil {
			return atc.Worker{}, nil, err
		}

		lip, err := localip.LocalIP()
		if err != nil {
			return atc.Worker{}, nil, err
		}

		cmd.Garden.Network.AdditionalDNSServers = append(
			cmd.Garden.Network.AdditionalDNSServers,
			guardiancmd.IPFlag(net.ParseIP(lip)),
		)

		members = append(members, grouper.Member{
			Name:   "dns-proxy",
			Runner: dnsProxyRunner,
		})
	}

	return worker, grouper.NewParallel(os.Interrupt, members), nil
}

func (cmd *WorkerCommand) extractResources(logger lager.Logger) ([]atc.WorkerResourceType, error) {
	var resourceTypes []atc.WorkerResourceType

	resourcesDir := cmd.assetPath("resources")

	infos, err := ioutil.ReadDir(resourcesDir)
	if err != nil {
		logger.Error("failed-to-list-resource-assets", err)
		return nil, err
	}

	for _, info := range infos {
		resourceType := info.Name()

		workerResourceType, err := cmd.extractResource(
			logger.Session("extract", lager.Data{"resource-type": resourceType}),
			resourcesDir,
			resourceType,
		)
		if err != nil {
			logger.Error("failed-to-extract-resource", err)
			return nil, err
		}

		resourceTypes = append(resourceTypes, workerResourceType)
	}

	return resourceTypes, nil
}

func (cmd *WorkerCommand) extractResource(
	logger lager.Logger,
	resourcesDir string,
	resourceType string,
) (atc.WorkerResourceType, error) {
	resourceImagesDir := cmd.assetPath("resource-images")
	tarBin := cmd.assetPath("bin", "tar")

	archive := filepath.Join(resourcesDir, resourceType, "rootfs.tar.gz")

	extractedDir := filepath.Join(resourceImagesDir, resourceType)

	rootfsDir := filepath.Join(extractedDir, "rootfs")
	okMarker := filepath.Join(extractedDir, "ok")

	var version string
	versionFile, err := os.Open(filepath.Join(resourcesDir, resourceType, "version"))
	if err != nil {
		logger.Error("failed-to-read-version", err)
		return atc.WorkerResourceType{}, err
	}

	_, err = fmt.Fscanf(versionFile, "%s", &version)
	if err != nil {
		logger.Error("failed-to-parse-version", err)
		return atc.WorkerResourceType{}, err
	}

	defer versionFile.Close()

	var privileged bool
	_, err = os.Stat(filepath.Join(resourcesDir, resourceType, "privileged"))
	if err == nil {
		privileged = true
	}

	_, err = os.Stat(okMarker)
	if err == nil {
		logger.Info("already-extracted")
	} else {
		logger.Info("extracting")

		err := os.RemoveAll(rootfsDir)
		if err != nil {
			logger.Error("failed-to-clear-out-existing-rootfs", err)
			return atc.WorkerResourceType{}, err
		}

		err = os.MkdirAll(rootfsDir, 0755)
		if err != nil {
			logger.Error("failed-to-create-rootfs-dir", err)
			return atc.WorkerResourceType{}, err
		}

		tar := exec.Command(tarBin, "-zxf", archive, "-C", rootfsDir)

		output, err := tar.CombinedOutput()
		if err != nil {
			logger.Error("failed-to-extract-resource", err, lager.Data{
				"output": string(output),
			})
			return atc.WorkerResourceType{}, err
		}

		ok, err := os.Create(okMarker)
		if err != nil {
			logger.Error("failed-to-create-ok-marker", err)
			return atc.WorkerResourceType{}, err
		}

		err = ok.Close()
		if err != nil {
			logger.Error("failed-to-close-ok-marker", err)
			return atc.WorkerResourceType{}, err
		}
	}

	return atc.WorkerResourceType{
		Type:       resourceType,
		Image:      rootfsDir,
		Version:    version,
		Privileged: privileged,
	}, nil
}

var ErrNotRoot = errors.New("worker must be run as root")

func (cmd *WorkerCommand) checkRoot() error {
	currentUser, err := user.Current()
	if err != nil {
		return err
	}

	if currentUser.Uid != "0" {
		return ErrNotRoot
	}

	return nil
}

func (cmd *WorkerCommand) dnsProxyRunner(logger lager.Logger) (ifrit.Runner, error) {
	server, err := cmd.Garden.DNS.Server()
	if err != nil {
		return nil, err
	}

	return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
		server.NotifyStartedFunc = func() {
			close(ready)
			logger.Info("started")
		}

		serveErr := make(chan error, 1)

		go func() {
			serveErr <- server.ListenAndServe()
		}()

		for {
			select {
			case err := <-serveErr:
				return err
			case <-signals:
				server.Shutdown()
			}
		}
	}), nil
}
<|endoftext|>


<commit_before>package command

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"os"
	"reflect"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/registry"
	zkr "github.com/funkygao/gafka/registry/zk"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	gio "github.com/funkygao/golib/io"
	"github.com/funkygao/golib/locking"
	"github.com/funkygao/golib/signal"
	log "github.com/funkygao/log4go"
	zklib "github.com/samuel/go-zookeeper/zk"
)

type Start struct {
	Ui  cli.Ui
	Cmd string

	zone       string
	root       string
	command    string
	logfile    string
	pubPort    int
	subPort    int
	manPort    int
	starting   bool
	forwardFor bool

	quitCh      chan struct{}
	zkzone      *zk.ZkZone
	lastServers BackendServers
}

func (this *Start) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("start", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.logfile, "log", defaultLogfile, "")
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&this.root, "p", defaultPrefix, "")
	cmdFlags.BoolVar(&this.forwardFor, "forwardfor", false, "")
	cmdFlags.IntVar(&this.pubPort, "pub", 10891, "")
	cmdFlags.IntVar(&this.subPort, "sub", 10892, "")
	cmdFlags.IntVar(&this.manPort, "man", 10893, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	lockFilename := fmt.Sprintf("%s/.lock", this.root)
	if locking.InstanceLocked(lockFilename) {
		panic(fmt.Sprintf("locked[%s] by another instance", lockFilename))
	}

	locking.LockInstance(lockFilename)

	err := os.Chdir(this.root)
	swalllow(err)

	this.command = fmt.Sprintf("%s/sbin/haproxy", this.root)
	if _, err := os.Stat(this.command); err != nil {
		panic(err)
	}

	this.setupLogging(this.logfile, "info", "panic")
	this.starting = true
	this.quitCh = make(chan struct{})
	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Info("ehaproxy[%s] got signal %s", gafka.BuildId, sig)
		this.shutdown()

		log.Info("removing %s", configFile)
		os.Remove(configFile)

		log.Info("removing lock[%s]", lockFilename)
		locking.UnlockInstance(lockFilename)

		close(this.quitCh)

		log.Info("ehaproxy[%s] shutdown complete", gafka.BuildId)
	}, syscall.SIGINT, syscall.SIGTERM)

	this.main()
	log.Close()

	return
}

func (this *Start) main() {
	ctx.LoadFromHome()
	this.zkzone = zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
	zkConnEvt, ok := this.zkzone.SessionEvents()
	if !ok {
		panic("someone stealing my events")
	}

	registry.Default = zkr.New(this.zkzone)

	log.Info("ehaproxy[%s] starting...", gafka.BuildId)

	zkConnected := false
	for {
		instances, instancesChange, err := registry.Default.WatchInstances()
		if err != nil {
			log.Error("zone[%s] %s", this.zkzone.Name(), err)
			time.Sleep(time.Second)
			continue
		}

		if zkConnected {
			if len(instances) > 0 {
				this.reload(instances)
			} else {
				log.Warn("backend all shutdown? skip this change")
			}
		}

		select {
		case <-this.quitCh:
			return

		case evt := <-zkConnEvt:
			if evt.State == zklib.StateHasSession && !zkConnected {
				log.Info("zk connected")
				zkConnected = true
			} else if zkConnected {
				log.Warn("zk jitter: %+v", evt)
			}

		case <-instancesChange:
			log.Info("instances changed!!")
		}
	}

}

func (this *Start) reload(kwInstances []string) {
	var servers = BackendServers{
		CpuNum:      ctx.NumCPU(),
		HaproxyRoot: this.root,
		ForwardFor:  this.forwardFor,
		PubPort:     this.pubPort,
		SubPort:     this.subPort,
		ManPort:     this.manPort,
	}
	servers.reset()
	for _, kwNode := range kwInstances {
		data, _, err := this.zkzone.Conn().Get(kwNode)
		if err != nil {
			log.Error("%s: %v", kwNode, err)
			continue
		}

		info := make(map[string]string)
		if err = json.Unmarshal([]byte(data), &info); err != nil {
			log.Error("%s: %v", data, err)
			continue
		}

		// pub
		if info["pub"] != "" {
			_, port, _ := net.SplitHostPort(info["pub"])
			be := Backend{
				Name: "p" + info["id"],
				Addr: info["pub"],
				Cpu:  info["cpu"],
				Port: port,
			}
			servers.Pub = append(servers.Pub, be)
		}

		// sub
		if info["sub"] != "" {
			_, port, _ := net.SplitHostPort(info["sub"])
			be := Backend{
				Name: "s" + info["id"],
				Addr: info["sub"],
				Cpu:  info["cpu"],
				Port: port,
			}
			servers.Sub = append(servers.Sub, be)
		}

		// man
		if info["man"] != "" {
			_, port, _ := net.SplitHostPort(info["man"])
			be := Backend{
				Name: "m" + info["id"],
				Addr: info["man"],
				Cpu:  info["cpu"],
				Port: port,
			}
			servers.Man = append(servers.Man, be)
		}

	}

	if servers.empty() {
		log.Warn("empty backend servers, all shutdown?")
		return
	}

	if reflect.DeepEqual(this.lastServers, servers) {
		log.Warn("backend servers stays unchanged")
		return
	}

	this.lastServers = servers
	if err := this.createConfigFile(servers); err != nil {
		log.Error(err)
		return
	}

	if err := this.reloadHAproxy(); err != nil {
		log.Error("reloading haproxy: %v", err)
		panic(err)
	}
}

func (this *Start) shutdown() {
	// kill haproxy
	log.Info("killling haproxy processes")

	f, e := os.Open(haproxyPidFile)
	swalllow(e)

	reader := bufio.NewReader(f)
	for {
		l, e := gio.ReadLine(reader)
		if e != nil {
			// EOF
			break
		}

		pid, _ := strconv.Atoi(string(l))
		p := &os.Process{
			Pid: pid,
		}
		if err := p.Kill(); err != nil {
			log.Error(err)
		} else {
			log.Info("haproxy[%d] terminated", pid)
		}
	}

	log.Info("removing %s", haproxyPidFile)
	os.Remove(haproxyPidFile)
}

func (this *Start) Synopsis() string {
	return fmt.Sprintf("Start %s system on localhost", this.Cmd)
}

func (this *Start) Help() string {
	help := fmt.Sprintf(`
Usage: %s start [options]

    Start %s system on localhost

Options:

    -z zone
      Default %s

    -forwardfor
      Default false.
      If true, haproxy will add X-Forwarded-For http header.

    -pub pub server listen port

    -sub sub server listen port

    -man manager server listen port

    -p directory prefix
      Default %s

    -log log file
      Default %s

`, this.Cmd, this.Cmd, ctx.ZkDefaultZone(), defaultPrefix, defaultLogfile)
	return strings.TrimSpace(help)
}
<commit_msg>os signal shows in upper case<commit_after>package command

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"os"
	"reflect"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/registry"
	zkr "github.com/funkygao/gafka/registry/zk"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	gio "github.com/funkygao/golib/io"
	"github.com/funkygao/golib/locking"
	"github.com/funkygao/golib/signal"
	log "github.com/funkygao/log4go"
	zklib "github.com/samuel/go-zookeeper/zk"
)

type Start struct {
	Ui  cli.Ui
	Cmd string

	zone       string
	root       string
	command    string
	logfile    string
	pubPort    int
	subPort    int
	manPort    int
	starting   bool
	forwardFor bool

	quitCh      chan struct{}
	zkzone      *zk.ZkZone
	lastServers BackendServers
}

func (this *Start) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("start", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.logfile, "log", defaultLogfile, "")
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&this.root, "p", defaultPrefix, "")
	cmdFlags.BoolVar(&this.forwardFor, "forwardfor", false, "")
	cmdFlags.IntVar(&this.pubPort, "pub", 10891, "")
	cmdFlags.IntVar(&this.subPort, "sub", 10892, "")
	cmdFlags.IntVar(&this.manPort, "man", 10893, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	lockFilename := fmt.Sprintf("%s/.lock", this.root)
	if locking.InstanceLocked(lockFilename) {
		panic(fmt.Sprintf("locked[%s] by another instance", lockFilename))
	}

	locking.LockInstance(lockFilename)

	err := os.Chdir(this.root)
	swalllow(err)

	this.command = fmt.Sprintf("%s/sbin/haproxy", this.root)
	if _, err := os.Stat(this.command); err != nil {
		panic(err)
	}

	this.setupLogging(this.logfile, "info", "panic")
	this.starting = true
	this.quitCh = make(chan struct{})
	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Info("ehaproxy[%s] got signal: %s", gafka.BuildId, strings.ToUpper(sig.String()))
		this.shutdown()

		log.Info("removing %s", configFile)
		os.Remove(configFile)

		log.Info("removing lock[%s]", lockFilename)
		locking.UnlockInstance(lockFilename)

		close(this.quitCh)

		log.Info("ehaproxy[%s] shutdown complete", gafka.BuildId)
	}, syscall.SIGINT, syscall.SIGTERM)

	this.main()
	log.Close()

	return
}

func (this *Start) main() {
	ctx.LoadFromHome()
	this.zkzone = zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone)))
	zkConnEvt, ok := this.zkzone.SessionEvents()
	if !ok {
		panic("someone stealing my events")
	}

	registry.Default = zkr.New(this.zkzone)

	log.Info("ehaproxy[%s] starting...", gafka.BuildId)

	zkConnected := false
	for {
		instances, instancesChange, err := registry.Default.WatchInstances()
		if err != nil {
			log.Error("zone[%s] %s", this.zkzone.Name(), err)
			time.Sleep(time.Second)
			continue
		}

		if zkConnected {
			if len(instances) > 0 {
				this.reload(instances)
			} else {
				log.Warn("backend all shutdown? skip this change")
			}
		}

		select {
		case <-this.quitCh:
			return

		case evt := <-zkConnEvt:
			if evt.State == zklib.StateHasSession && !zkConnected {
				log.Info("zk connected")
				zkConnected = true
			} else if zkConnected {
				log.Warn("zk jitter: %+v", evt)
			}

		case <-instancesChange:
			log.Info("instances changed!!")
		}
	}

}

func (this *Start) reload(kwInstances []string) {
	var servers = BackendServers{
		CpuNum:      ctx.NumCPU(),
		HaproxyRoot: this.root,
		ForwardFor:  this.forwardFor,
		PubPort:     this.pubPort,
		SubPort:     this.subPort,
		ManPort:     this.manPort,
	}
	servers.reset()
	for _, kwNode := range kwInstances {
		data, _, err := this.zkzone.Conn().Get(kwNode)
		if err != nil {
			log.Error("%s: %v", kwNode, err)
			continue
		}

		info := make(map[string]string)
		if err = json.Unmarshal([]byte(data), &info); err != nil {
			log.Error("%s: %v", data, err)
			continue
		}

		// pub
		if info["pub"] != "" {
			_, port, _ := net.SplitHostPort(info["pub"])
			be := Backend{
				Name: "p" + info["id"],
				Addr: info["pub"],
				Cpu:  info["cpu"],
				Port: port,
			}
			servers.Pub = append(servers.Pub, be)
		}

		// sub
		if info["sub"] != "" {
			_, port, _ := net.SplitHostPort(info["sub"])
			be := Backend{
				Name: "s" + info["id"],
				Addr: info["sub"],
				Cpu:  info["cpu"],
				Port: port,
			}
			servers.Sub = append(servers.Sub, be)
		}

		// man
		if info["man"] != "" {
			_, port, _ := net.SplitHostPort(info["man"])
			be := Backend{
				Name: "m" + info["id"],
				Addr: info["man"],
				Cpu:  info["cpu"],
				Port: port,
			}
			servers.Man = append(servers.Man, be)
		}

	}

	if servers.empty() {
		log.Warn("empty backend servers, all shutdown?")
		return
	}

	if reflect.DeepEqual(this.lastServers, servers) {
		log.Warn("backend servers stays unchanged")
		return
	}

	this.lastServers = servers
	if err := this.createConfigFile(servers); err != nil {
		log.Error(err)
		return
	}

	if err := this.reloadHAproxy(); err != nil {
		log.Error("reloading haproxy: %v", err)
		panic(err)
	}
}

func (this *Start) shutdown() {
	// kill haproxy
	log.Info("killling haproxy processes")

	f, e := os.Open(haproxyPidFile)
	swalllow(e)

	reader := bufio.NewReader(f)
	for {
		l, e := gio.ReadLine(reader)
		if e != nil {
			// EOF
			break
		}

		pid, _ := strconv.Atoi(string(l))
		p := &os.Process{
			Pid: pid,
		}
		if err := p.Kill(); err != nil {
			log.Error(err)
		} else {
			log.Info("haproxy[%d] terminated", pid)
		}
	}

	log.Info("removing %s", haproxyPidFile)
	os.Remove(haproxyPidFile)
}

func (this *Start) Synopsis() string {
	return fmt.Sprintf("Start %s system on localhost", this.Cmd)
}

func (this *Start) Help() string {
	help := fmt.Sprintf(`
Usage: %s start [options]

    Start %s system on localhost

Options:

    -z zone
      Default %s

    -forwardfor
      Default false.
      If true, haproxy will add X-Forwarded-For http header.

    -pub pub server listen port

    -sub sub server listen port

    -man manager server listen port

    -p directory prefix
      Default %s

    -log log file
      Default %s

`, this.Cmd, this.Cmd, ctx.ZkDefaultZone(), defaultPrefix, defaultLogfile)
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"internal/syscall/windows/registry"
	"nvm/web"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/blang/semver"
	"github.com/coreybutler/go-fsutil"
	"github.com/gen2brain/dlgs"
	"golang.org/x/sys/windows/registry"
)

var version = "1.1.9"

func main() {
	// baseVersion := version
	args := os.Args

	if len(args) > 1 {
		version = args[1]
	}

	root := os.Getenv("NVM_HOME")
	if root == "" {
		fmt.Println("Cannot find NVM_HOME")
		os.Exit(1)
	}

	exe := filepath.Join(root, "nvm.exe")
	currentNvmVersion, err := semver.Make(strings.TrimSpace(run(exe, "version")))
	if err != nil {
		fmt.Println("NVM for Windows installation not found in " + root)
		os.Exit(1)
	}

	err = currentNvmVersion.Validate()
	if err != nil {
		fmt.Println("NVM for Windows installation not found in " + root)
		os.Exit(1)
	}

	if !contains(args, "/S") && !contains(args, "/s") {
		item, _, err := dlgs.Entry("NVM Upgrade", "Upgrade to which version?", version)
		if err != nil || len(strings.TrimSpace(item)) == 0 {
			fmt.Println("Upgrade cancelled")
			os.Exit(0)
		}
		version = item
	}

	newNvmVersion, err := semver.Make(version)
	if err != nil {
		fmt.Println("Version " + version + " is not recognized or does not exist")
		os.Exit(1)
	}

	err = newNvmVersion.Validate()
	if err != nil {
		fmt.Println("Invalid version: " + version)
		os.Exit(1)
	}

	if newNvmVersion.LTE(currentNvmVersion) {
		fmt.Printf("No upgrade necessary. Already running v%s.", currentNvmVersion.String())
		os.Exit(0)
	}

	tmpdir := filepath.Join(root, "tmp")
	zipfile := filepath.Join(tmpdir, "install.zip")

	os.RemoveAll(tmpdir)
	fsutil.Touch(tmpdir)

	fmt.Println("Downloading NVM for Windows v" + version)
	success := web.Download("https://github.com/coreybutler/nvm-windows/releases/download/"+version+"/nvm-noinstall.zip", zipfile)
	if !success {
		os.RemoveAll(tmpdir)
		os.Exit(1)
	}

	fmt.Println(" - Extracting")
	web.Unzip(zipfile, tmpdir)
	os.RemoveAll(zipfile)
	fmt.Println(" - Updating")
	fsutil.Move(tmpdir, root)
	os.RemoveAll(tmpdir)

	version := strings.TrimSpace(run(exe, "version"))
	key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\40078385-F676-4C61-9A9C-F9028599D6D3_is1`, registry.QUERY_VALUE)
	if err == nil {
		defer key.Close()
		key.SetStringValue("DisplayVersion", version)
		key.SetStringValue("DisplayName", "NVM for Windows "+version)

		current := time.Now()
		key.SetStringValue("InstallDate", current.Format("20060102"))
	}

	fmt.Printf("\nUpgraded to NVM for Windows v%v", version)
}

func run(command ...string) string {
	base := command[0]
	args := command[1:]
	cmd := exec.Command(base, args...)

	var out bytes.Buffer
	cmd.Stdout = &out

	err := cmd.Run()

	if err != nil {
		return err.Error()
	}

	return out.String()
}

func contains(src []string, pattern string) bool {
	for _, arg := range src {
		if arg == pattern {
			return true
		}
	}

	return false
}
<commit_msg>Removed reference to internal registry module<commit_after>package main

import (
	"bytes"
	"fmt"
	"nvm/web"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/blang/semver"
	"github.com/coreybutler/go-fsutil"
	"github.com/gen2brain/dlgs"
	"golang.org/x/sys/windows/registry"
)

var version = "1.1.9"

func main() {
	// baseVersion := version
	args := os.Args

	if len(args) > 1 {
		version = args[1]
	}

	root := os.Getenv("NVM_HOME")
	if root == "" {
		fmt.Println("Cannot find NVM_HOME")
		os.Exit(1)
	}

	exe := filepath.Join(root, "nvm.exe")
	currentNvmVersion, err := semver.Make(strings.TrimSpace(run(exe, "version")))
	if err != nil {
		fmt.Println("NVM for Windows installation not found in " + root)
		os.Exit(1)
	}

	err = currentNvmVersion.Validate()
	if err != nil {
		fmt.Println("NVM for Windows installation not found in " + root)
		os.Exit(1)
	}

	if !contains(args, "/S") && !contains(args, "/s") {
		item, _, err := dlgs.Entry("NVM Upgrade", "Upgrade to which version?", version)
		if err != nil || len(strings.TrimSpace(item)) == 0 {
			fmt.Println("Upgrade cancelled")
			os.Exit(0)
		}
		version = item
	}

	newNvmVersion, err := semver.Make(version)
	if err != nil {
		fmt.Println("Version " + version + " is not recognized or does not exist")
		os.Exit(1)
	}

	err = newNvmVersion.Validate()
	if err != nil {
		fmt.Println("Invalid version: " + version)
		os.Exit(1)
	}

	if newNvmVersion.LTE(currentNvmVersion) {
		fmt.Printf("No upgrade necessary. Already running v%s.", currentNvmVersion.String())
		os.Exit(0)
	}

	tmpdir := filepath.Join(root, "tmp")
	zipfile := filepath.Join(tmpdir, "install.zip")

	os.RemoveAll(tmpdir)
	fsutil.Touch(tmpdir)

	fmt.Println("Downloading NVM for Windows v" + version)
	success := web.Download("https://github.com/coreybutler/nvm-windows/releases/download/"+version+"/nvm-noinstall.zip", zipfile)
	if !success {
		os.RemoveAll(tmpdir)
		os.Exit(1)
	}

	fmt.Println(" - Extracting")
	web.Unzip(zipfile, tmpdir)
	os.RemoveAll(zipfile)
	fmt.Println(" - Updating")
	fsutil.Move(tmpdir, root)
	os.RemoveAll(tmpdir)

	version := strings.TrimSpace(run(exe, "version"))
	key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\40078385-F676-4C61-9A9C-F9028599D6D3_is1`, registry.QUERY_VALUE)
	if err == nil {
		defer key.Close()
		key.SetStringValue("DisplayVersion", version)
		key.SetStringValue("DisplayName", "NVM for Windows "+version)

		current := time.Now()
		key.SetStringValue("InstallDate", current.Format("20060102"))
	}

	fmt.Printf("\nUpgraded to NVM for Windows v%v", version)
}

func run(command ...string) string {
	base := command[0]
	args := command[1:]
	cmd := exec.Command(base, args...)

	var out bytes.Buffer
	cmd.Stdout = &out

	err := cmd.Run()

	if err != nil {
		return err.Error()
	}

	return out.String()
}

func contains(src []string, pattern string) bool {
	for _, arg := range src {
		if arg == pattern {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package contract

import (
	"github.com/smartystreets/goconvey/web/server/parser"
	"net/http"
	"path/filepath"
)

type (
	Server interface {
		ReceiveUpdate(*parser.CompleteOutput)
		Watch(writer http.ResponseWriter, request *http.Request)
		Status(writer http.ResponseWriter, request *http.Request)
		Results(writer http.ResponseWriter, request *http.Request)
		Execute(writer http.ResponseWriter, request *http.Request)
	}

	Scanner interface {
		Scan(root string) (changed bool)
	}

	Watcher interface {
		// contains the FileSystem

		Adjust(root string) error

		Deletion(folder string)
		Creation(folder string)

		Ignore(folder string) error
		Reinstate(folder string) error

		WatchedFolders() []*Package
	}

	Executor interface {
		// contains the executor.Parser
		// contains the Server
		// contains the Shell

		ExecuteTests([]*Package) *parser.CompleteOutput
		IsRunning() bool
	}

	FileSystem interface {
		Walk(root string, step filepath.WalkFunc)
		Exists(directory string) bool
	}

	Shell interface {
		Execute(name string, args ...string) (output string, err error)
	}
)

type Package struct {
	Active       bool
	Path         string
	Name         string
	RawOutput    string
	ParsedOutput *parser.PackageResult
}
<commit_msg>Added query methods to public watcher contract.<commit_after>package contract

import (
	"github.com/smartystreets/goconvey/web/server/parser"
	"net/http"
	"path/filepath"
)

type (
	Server interface {
		ReceiveUpdate(*parser.CompleteOutput)
		Watch(writer http.ResponseWriter, request *http.Request)
		Status(writer http.ResponseWriter, request *http.Request)
		Results(writer http.ResponseWriter, request *http.Request)
		Execute(writer http.ResponseWriter, request *http.Request)
	}

	Scanner interface {
		Scan(root string) (changed bool)
	}

	Watcher interface {
		// contains the FileSystem

		Adjust(root string) error

		Deletion(folder string)
		Creation(folder string)

		Ignore(folder string) error
		Reinstate(folder string) error

		WatchedFolders() []*Package
		IsActive(folder string) bool
		IsIgnored(folder string) bool
		IsWatched(folder string) bool
	}

	Executor interface {
		// contains the executor.Parser
		// contains the Server
		// contains the Shell

		ExecuteTests([]*Package) *parser.CompleteOutput
		IsRunning() bool
	}

	FileSystem interface {
		Walk(root string, step filepath.WalkFunc)
		Exists(directory string) bool
	}

	Shell interface {
		Execute(name string, args ...string) (output string, err error)
	}
)

type Package struct {
	Active       bool
	Path         string
	Name         string
	RawOutput    string
	ParsedOutput *parser.PackageResult
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http" // Provides HTTP client and server implementations.

	"github.com/blockfreight/go-bftx/lib/app/bf_tx"

	"github.com/blockfreight/go-bftx/api/transaction"
	"github.com/blockfreight/go-bftx/lib/pkg/leveldb" // Provides some useful functions to work with LevelDB.
	"github.com/graphql-go/graphql"
)

func Start() error {
	http.HandleFunc("/graphql", graphRoute)
	fmt.Println("Now server is running on port 12345")
	fmt.Println("Test with Get      : curl -g 'http://localhost:12345/graphql?query={transaction(id:<BFTX-ID>){Id}}'")
	return http.ListenAndServe(":12345", nil)

}

var issueDetails = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "IssueDetails",
		Fields: graphql.InputObjectConfigFieldMap{
			"PlaceOfIssue": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"DateOfIssue": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var masterInfo = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "MasterInfo",
		Fields: graphql.InputObjectConfigFieldMap{
			"FirstName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"LastName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Sig": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var agentForMaster = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "AgentForMaster",
		Fields: graphql.InputObjectConfigFieldMap{
			"FirstName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"LastName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Sig": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var agentForOwner = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "AgentForOwner",
		Fields: graphql.InputObjectConfigFieldMap{
			"FirstName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"LastName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Sig": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"ConditionsForCarriage": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var propertiesType = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "Properties",
		Fields: graphql.InputObjectConfigFieldMap{
			"Shipper": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"BolNum": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"RefNum": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"Consignee": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Vessel": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"PortOfLoading": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"PortOfDischarge": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"NotifyAddress": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"DescOfGoods": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"GrossWeight": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"FreightPayableAmt": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"FreightAdvAmt": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"GeneralInstructions": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"DateShipped": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"IssueDetails": &graphql.InputObjectFieldConfig{
				Type: issueDetails,
			},
			"NumBol": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"MasterInfo": &graphql.InputObjectFieldConfig{
				Type: masterInfo,
			},
			"AgentForMaster": &graphql.InputObjectFieldConfig{
				Type: agentForMaster,
			},
			"AgentForOwner": &graphql.InputObjectFieldConfig{
				Type: agentForOwner,
			},
		},
	},
)

var transactionType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Transaction",
		Fields: graphql.Fields{
			"Id": &graphql.Field{
				Type: graphql.String,
			},
			"Type": &graphql.Field{
				Type: graphql.String,
			},
			"Verified": &graphql.Field{
				Type: graphql.Boolean,
			},
			"Transmitted": &graphql.Field{
				Type: graphql.Boolean,
			},
			"Properties": &graphql.Field{
				Type: propertiesType,
			},
		},
	},
)

var schema, _ = graphql.NewSchema(
	graphql.SchemaConfig{
		Query:    queryType,
		Mutation: mutationType,
	},
)

func graphRoute(w http.ResponseWriter, r *http.Request) {
	query, _ := ioutil.ReadAll(r.Body)
	result := executeQuery(string(query), schema)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(result)
}

var queryType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Query",
		Fields: graphql.Fields{
			"transaction": &graphql.Field{
				Type: transactionType,
				Args: graphql.FieldConfigArgument{
					"id": &graphql.ArgumentConfig{
						Type: graphql.String,
					},
				},
				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
					bftxID, isOK := p.Args["id"].(string)
					if !isOK {
						return nil, nil
					}

					bftx, err := leveldb.GetBfTx(bftxID)
					if err != nil {
						return nil, nil
					}
					return bftx, nil
				},
			},
		},
	})

var mutationType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Mutation",
		Fields: graphql.Fields{
			"constructBFTX": &graphql.Field{
				Type: transactionType,
				Args: graphql.FieldConfigArgument{
					"Type": &graphql.ArgumentConfig{
						Type: graphql.String,
					},
					"Properties": &graphql.ArgumentConfig{
						Description: "Transaction properties.",
						Type:        propertiesType,
					},
				},
				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
					bftx := bf_tx.BF_TX{}
					jsonProperties, err := json.Marshal(p.Args)
					if err = json.Unmarshal([]byte(jsonProperties), &bftx); err != nil {
						fmt.Printf("err")
						fmt.Print(err)
					}
					fmt.Printf("%+v\n", bftx)

					bftx, err = transaction.ConstructBfTx(bftx)
					if err != nil {
						return nil, err
					}

					return bftx, err
				},
			},
		},
	},
)

func executeQuery(query string, schema graphql.Schema) *graphql.Result {

	result := graphql.Do(graphql.Params{
		Schema:        schema,
		RequestString: query,
	})

	return result
}
<commit_msg>Get and save transaction working<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http" // Provides HTTP client and server implementations.

	"github.com/blockfreight/go-bftx/lib/app/bf_tx"

	"github.com/blockfreight/go-bftx/api/transaction"
	"github.com/blockfreight/go-bftx/lib/pkg/leveldb" // Provides some useful functions to work with LevelDB.
	"github.com/graphql-go/graphql"
)

func Start() error {
	http.HandleFunc("/graphql", graphRoute)
	fmt.Println("Now server is running on port 12345")
	fmt.Println("Test with Get      : curl -g 'http://localhost:12345/graphql?query={transaction(id:<BFTX-ID>){Id}}'")
	return http.ListenAndServe(":12345", nil)

}

var issueDetailsInput = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "IssueDetails",
		Fields: graphql.InputObjectConfigFieldMap{
			"PlaceOfIssue": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"DateOfIssue": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var issueDetails = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "IssueDetails",
		Fields: graphql.Fields{
			"PlaceOfIssue": &graphql.Field{
				Type: graphql.String,
			},
			"DateOfIssue": &graphql.Field{
				Type: graphql.String,
			},
		},
	},
)

var masterInfoInput = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "MasterInfo",
		Fields: graphql.InputObjectConfigFieldMap{
			"FirstName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"LastName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Sig": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var masterInfo = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "MasterInfo",
		Fields: graphql.Fields{
			"FirstName": &graphql.Field{
				Type: graphql.String,
			},
			"LastName": &graphql.Field{
				Type: graphql.String,
			},
			"Sig": &graphql.Field{
				Type: graphql.String,
			},
		},
	},
)

var agentForMasterInput = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "AgentForMaster",
		Fields: graphql.InputObjectConfigFieldMap{
			"FirstName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"LastName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Sig": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var agentForMaster = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "AgentForMaster",
		Fields: graphql.Fields{
			"FirstName": &graphql.Field{
				Type: graphql.String,
			},
			"LastName": &graphql.Field{
				Type: graphql.String,
			},
			"Sig": &graphql.Field{
				Type: graphql.String,
			},
		},
	},
)

var agentForOwnerInput = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "AgentForOwner",
		Fields: graphql.InputObjectConfigFieldMap{
			"FirstName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"LastName": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Sig": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"ConditionsForCarriage": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	},
)

var agentForOwner = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "agentForOwner",
		Fields: graphql.Fields{
			"FirstName": &graphql.Field{
				Type: graphql.String,
			},
			"LastName": &graphql.Field{
				Type: graphql.String,
			},
			"Sig": &graphql.Field{
				Type: graphql.String,
			},
			"ConditionsForCarriage": &graphql.Field{
				Type: graphql.String,
			},
		},
	},
)

var propertiesType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Properties",
		Fields: graphql.Fields{
			"Shipper": &graphql.Field{
				Type: graphql.String,
			},
			"BolNum": &graphql.Field{
				Type: graphql.Int,
			},
			"RefNum": &graphql.Field{
				Type: graphql.Int,
			},
			"Consignee": &graphql.Field{
				Type: graphql.String,
			},
			"Vessel": &graphql.Field{
				Type: graphql.Int,
			},
			"PortOfLoading": &graphql.Field{
				Type: graphql.Int,
			},
			"PortOfDischarge": &graphql.Field{
				Type: graphql.Int,
			},
			"NotifyAddress": &graphql.Field{
				Type: graphql.String,
			},
			"DescOfGoods": &graphql.Field{
				Type: graphql.String,
			},
			"GrossWeight": &graphql.Field{
				Type: graphql.Int,
			},
			"FreightPayableAmt": &graphql.Field{
				Type: graphql.Int,
			},
			"FreightAdvAmt": &graphql.Field{
				Type: graphql.Int,
			},
			"GeneralInstructions": &graphql.Field{
				Type: graphql.String,
			},
			"DateShipped": &graphql.Field{
				Type: graphql.Int,
			},
			"IssueDetails": &graphql.Field{
				Type: issueDetails,
			},
			"NumBol": &graphql.Field{
				Type: graphql.Int,
			},
			"MasterInfo": &graphql.Field{
				Type: masterInfo,
			},
			"AgentForMaster": &graphql.Field{
				Type: agentForMaster,
			},
			"AgentForOwner": &graphql.Field{
				Type: agentForOwner,
			},
		},
	},
)

var propertiesInput = graphql.NewInputObject(
	graphql.InputObjectConfig{
		Name: "Properties",
		Fields: graphql.InputObjectConfigFieldMap{
			"Shipper": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"BolNum": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"RefNum": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"Consignee": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"Vessel": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"PortOfLoading": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"PortOfDischarge": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"NotifyAddress": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"DescOfGoods": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"GrossWeight": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"FreightPayableAmt": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"FreightAdvAmt": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"GeneralInstructions": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"DateShipped": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
			"IssueDetails": &graphql.InputObjectFieldConfig{
				Type: issueDetailsInput,
			},
			"NumBol": &graphql.InputObjectFieldConfig{
				Type: graphql.Int,
			},
			"MasterInfo": &graphql.InputObjectFieldConfig{
				Type: masterInfoInput,
			},
			"AgentForMaster": &graphql.InputObjectFieldConfig{
				Type: agentForMasterInput,
			},
			"AgentForOwner": &graphql.InputObjectFieldConfig{
				Type: agentForOwnerInput,
			},
		},
	},
)

var transactionType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Transaction",
		Fields: graphql.Fields{
			"Id": &graphql.Field{
				Type: graphql.String,
			},
			"Type": &graphql.Field{
				Type: graphql.String,
			},
			"Verified": &graphql.Field{
				Type: graphql.Boolean,
			},
			"Transmitted": &graphql.Field{
				Type: graphql.Boolean,
			},
			"Properties": &graphql.Field{
				Type: propertiesType,
			},
		},
	},
)

var schema, _ = graphql.NewSchema(
	graphql.SchemaConfig{
		Query:    queryType,
		Mutation: mutationType,
	},
)

func graphRoute(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query().Get("query")
	if query == "" {
		params, _ := ioutil.ReadAll(r.Body)
		query = string(params)
	}

	result := executeQuery(string(query), schema)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(result)
}

var queryType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Query",
		Fields: graphql.Fields{
			"transaction": &graphql.Field{
				Type: transactionType,
				Args: graphql.FieldConfigArgument{
					"id": &graphql.ArgumentConfig{
						Type: graphql.String,
					},
				},
				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
					bftxID, isOK := p.Args["id"].(string)
					if !isOK {
						return nil, nil
					}

					bftx, err := leveldb.GetBfTx(bftxID)
					if err != nil {
						return nil, nil
					}

					fmt.Printf("%+v\n", bftx)

					return bftx, nil
				},
			},
		},
	})

var mutationType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Mutation",
		Fields: graphql.Fields{
			"constructBFTX": &graphql.Field{
				Type: transactionType,
				Args: graphql.FieldConfigArgument{
					"Type": &graphql.ArgumentConfig{
						Type: graphql.String,
					},
					"Properties": &graphql.ArgumentConfig{
						Description: "Transaction properties.",
						Type:        propertiesInput,
					},
				},
				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
					bftx := bf_tx.BF_TX{}
					jsonProperties, err := json.Marshal(p.Args)
					if err = json.Unmarshal([]byte(jsonProperties), &bftx); err != nil {
						fmt.Printf("err")
						fmt.Print(err)
					}
					fmt.Printf("%+v\n", bftx)

					bftx, err = transaction.ConstructBfTx(bftx)
					if err != nil {
						return nil, err
					}

					return bftx, err
				},
			},
		},
	},
)

func executeQuery(query string, schema graphql.Schema) *graphql.Result {

	result := graphql.Do(graphql.Params{
		Schema:        schema,
		RequestString: query,
	})

	return result
}
<|endoftext|>


<commit_before>package engine

import (
	"container/ring"
	"fmt"
	"strconv"
	"sync"

	"github.com/sai-lab/mouryou/lib/convert"
	"github.com/sai-lab/mouryou/lib/logger"
	"github.com/sai-lab/mouryou/lib/models"
	"github.com/sai-lab/mouryou/lib/monitor"
	"github.com/sai-lab/mouryou/lib/mutex"
)

func Initialize(config *models.Config) {
	for name, machine := range config.Cluster.VirtualMachines {
		var st monitor.State
		st.Name = name
		if config.DevelopLogLevel >= 4 {
			logger.PrintPlace("Machine ID: " + strconv.Itoa(machine.Id) + ", Machine Name: " + name)
		}

		err := config.Cluster.LoadBalancer.ChangeWeight(name, machine.Weight)
		if err != nil {
			fmt.Println("Error is occured! Cannot change weight. Error is : " + fmt.Sprint(err))
			break
		}
		st.Weight = machine.Weight
		if config.DevelopLogLevel >= 4 {
			logger.PrintPlace("Machine ID: " + strconv.Itoa(machine.Id) + ", Machine Name: " + name)
		}

		if config.ContainID(machine.Id) {
			if config.DevelopLogLevel > 1 {
				logger.PrintPlace("LogLevel 1 : set booted up " + " Machine Name: " + name +
					" Weight: " + strconv.Itoa(machine.Weight))
			}
			st.Info = "booted up"
			totalWeight += machine.Weight
		} else {
			st.Info = "shutted down"
			if config.DevelopLogLevel > 1 {
				logger.PrintPlace("LogLevel 1 : set shutted down " + " Machine Name: " + name +
					" Weight: " + strconv.Itoa(machine.Weight))
			}
		}
		monitor.States = append(monitor.States, st)
	}
}

func WeightOperator(config *models.Config) {
	var mu sync.RWMutex

	r := ring.New(LING_SIZE)

	mu.RLock()
	defer mu.RUnlock()

	for d := range monitor.DataCh {
		loadStates := map[string]int{}
		highLoads := map[string]int{}
		lowLoads := map[string]int{}
		weights := map[string]int{}
		cluster := config.Cluster
		weights["weights"] = -1

		for _, state := range monitor.States {
			if state.Name != "" {
				loadStates[state.Name] = 0
				if config.DevelopLogLevel >= 5 {
					logger.PrintPlace("state Name: " + state.Name + ", state weight: " + strconv.Itoa(state.Weight))
				}
				if state.Weight != 0 && state.Info == "booted up" {
					weights[state.Name] = state.Weight
				}
			}
		}

		r.Value = d
		r = r.Next()
		r.Do(func(v interface{}) {
			if v != nil {
				for _, ds := range v.([]monitor.Data) {
					loadStates[ds.Name] += LoadCheck(ds, cluster.VirtualMachines[ds.Name].Average, models.Threshold)
				}
			}
		})

		// check server load
		for k, v := range loadStates {
			if v < -5 {
				lowLoads[k] = v
			} else if v > 5 {
				highLoads[k] = v
			}
		}

		if len(highLoads) > 0 && len(lowLoads) > 0 {
			for name, _ := range lowLoads {
				FireChangeWeight(config, name, 5)
				weights[name] = weights[name] + 5
			}
			for name, _ := range highLoads {
				if weights[name] <= 5 {
					continue
				}
				FireChangeWeight(config, name, -5)
				weights[name] = weights[name] - 5
			}
		}

		ar := convert.MapToArray(weights)
		logger.Write(ar)
		logger.Print(ar)
	}
}

func LoadCheck(ds monitor.Data, average int, threshold float64) int {
	loadState := 0

	if ds.Throughput != 0 {
		if ds.Throughput < average && ds.CPU <= 40 {
			// Throughput is low && Using CPU rate is low.
			loadState -= 1
		} else if (float64(ds.Throughput) < float64(average)*1.3 || float64(ds.Throughput) > float64(average)*0.8) && ds.CPU >= 90 {
			// At first glance, throuput is noting bad but using CPU rate is high.
			loadState += 1
		}
	}

	if ds.Operating >= threshold {
		// Operating rate is high.
		loadState += 1
	} else if ds.Operating <= 0.3 && ds.CPU <= 50 {
		// Operating rate is low && Using CPU rate is low.
		loadState -= 1
	}

	return loadState
}

func FireChangeWeight(config *models.Config, name string, w int) {
	var mu sync.RWMutex
	var err error

	mu.RLock()
	defer mu.RUnlock()
	for _, state := range monitor.States {
		if state.Name == name {
			if w < 0 && state.Weight <= 5 {
				fmt.Println(state.Name + " is low weight")
				break
			}
			s := monitor.State{state.Name, state.Weight, state.Info}
			s.Weight = state.Weight + w
			logger.PrintPlace("-------------------- ChangeWeight " + s.Name + " -----------------------")
			err = config.Cluster.LoadBalancer.ChangeWeight(s.Name, s.Weight)
			if err != nil {
				fmt.Println("Error is occured! Cannot change weight. Error is : " + fmt.Sprint(err))
				break
			}

			if monitor.StateCh != nil {
				monitor.StateCh <- s
			} else {
				fmt.Println("statusCh is nil")
			}
			mutex.Write(&totalWeight, &totalWeightMutex, totalWeight+w)
			break
		}
	}
}
<commit_msg>add debug comment<commit_after>package engine

import (
	"container/ring"
	"fmt"
	"strconv"
	"sync"

	"github.com/sai-lab/mouryou/lib/convert"
	"github.com/sai-lab/mouryou/lib/logger"
	"github.com/sai-lab/mouryou/lib/models"
	"github.com/sai-lab/mouryou/lib/monitor"
	"github.com/sai-lab/mouryou/lib/mutex"
)

func Initialize(config *models.Config) {
	for name, machine := range config.Cluster.VirtualMachines {
		var st monitor.State
		st.Name = name
		if config.DevelopLogLevel >= 4 {
			logger.PrintPlace("Machine ID: " + strconv.Itoa(machine.Id) + ", Machine Name: " + name)
		}

		err := config.Cluster.LoadBalancer.ChangeWeight(name, machine.Weight)
		if err != nil {
			fmt.Println("Error is occured! Cannot change weight. Error is : " + fmt.Sprint(err))
			break
		}
		st.Weight = machine.Weight
		if config.DevelopLogLevel >= 4 {
			logger.PrintPlace("Machine ID: " + strconv.Itoa(machine.Id) + ", Machine Name: " + name)
		}

		if config.ContainID(machine.Id) {
			if config.DevelopLogLevel > 1 {
				logger.PrintPlace("LogLevel 1 : set booted up " + " Machine Name: " + name +
					" Weight: " + strconv.Itoa(machine.Weight))
			}
			st.Info = "booted up"
			totalWeight += machine.Weight
		} else {
			st.Info = "shutted down"
			if config.DevelopLogLevel > 1 {
				logger.PrintPlace("LogLevel 1 : set shutted down " + " Machine Name: " + name +
					" Weight: " + strconv.Itoa(machine.Weight))
			}
		}
		monitor.States = append(monitor.States, st)
	}
}

func WeightOperator(config *models.Config) {
	var mu sync.RWMutex

	r := ring.New(LING_SIZE)

	mu.RLock()
	defer mu.RUnlock()

	for d := range monitor.DataCh {
		loadStates := map[string]int{}
		highLoads := map[string]int{}
		lowLoads := map[string]int{}
		weights := map[string]int{}
		cluster := config.Cluster
		weights["weights"] = -1

		for _, state := range monitor.States {
			if state.Name != "" {
				loadStates[state.Name] = 0
				if config.DevelopLogLevel >= 5 {
					logger.PrintPlace("state Name: " + state.Name + ", state weight: " + strconv.Itoa(state.Weight))
				}
				if state.Weight != 0 && state.Info == "booted up" {
					weights[state.Name] = state.Weight
				}
			}
		}

		logger.PrintPlace("~~~~~~~~~~~~~~~Here!!!!!!~~~~~~~~~~~~~~~~~~~^")

		r.Value = d
		r = r.Next()
		r.Do(func(v interface{}) {
			if v != nil {
				for _, ds := range v.([]monitor.Data) {
					loadStates[ds.Name] += LoadCheck(ds, cluster.VirtualMachines[ds.Name].Average, models.Threshold)
				}
			}
		})

		// check server load
		for k, v := range loadStates {
			if v < -5 {
				lowLoads[k] = v
			} else if v > 5 {
				highLoads[k] = v
			}
		}

		if len(highLoads) > 0 && len(lowLoads) > 0 {
			for name, _ := range lowLoads {
				FireChangeWeight(config, name, 5)
				weights[name] = weights[name] + 5
			}
			for name, _ := range highLoads {
				if weights[name] <= 5 {
					continue
				}
				FireChangeWeight(config, name, -5)
				weights[name] = weights[name] - 5
			}
		}

		ar := convert.MapToArray(weights)
		logger.Write(ar)
		logger.Print(ar)
	}
}

func LoadCheck(ds monitor.Data, average int, threshold float64) int {
	loadState := 0

	if ds.Throughput != 0 {
		if ds.Throughput < average && ds.CPU <= 40 {
			// Throughput is low && Using CPU rate is low.
			loadState -= 1
		} else if (float64(ds.Throughput) < float64(average)*1.3 || float64(ds.Throughput) > float64(average)*0.8) && ds.CPU >= 90 {
			// At first glance, throuput is noting bad but using CPU rate is high.
			loadState += 1
		}
	}

	if ds.Operating >= threshold {
		// Operating rate is high.
		loadState += 1
	} else if ds.Operating <= 0.3 && ds.CPU <= 50 {
		// Operating rate is low && Using CPU rate is low.
		loadState -= 1
	}

	return loadState
}

func FireChangeWeight(config *models.Config, name string, w int) {
	var mu sync.RWMutex
	var err error

	mu.RLock()
	defer mu.RUnlock()
	for _, state := range monitor.States {
		if state.Name == name {
			if w < 0 && state.Weight <= 5 {
				fmt.Println(state.Name + " is low weight")
				break
			}
			s := monitor.State{state.Name, state.Weight, state.Info}
			s.Weight = state.Weight + w
			logger.PrintPlace("-------------------- ChangeWeight " + s.Name + " -----------------------")
			err = config.Cluster.LoadBalancer.ChangeWeight(s.Name, s.Weight)
			if err != nil {
				fmt.Println("Error is occured! Cannot change weight. Error is : " + fmt.Sprint(err))
				break
			}

			if monitor.StateCh != nil {
				monitor.StateCh <- s
			} else {
				fmt.Println("statusCh is nil")
			}
			mutex.Write(&totalWeight, &totalWeightMutex, totalWeight+w)
			break
		}
	}
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package exec

import (
	"context"
	"reflect"
	"testing"
	"time"

	"github.com/apache/beam/sdks/go/pkg/beam/core/funcx"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/mtime"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/window"
	"github.com/apache/beam/sdks/go/pkg/beam/core/typex"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/reflectx"
)

// TestInvoke verifies the the various forms of input to Invoke are handled correctly.
func TestInvoke(t *testing.T) {
	tests := []struct {
		Fn       interface{}
		Opt      *MainInput
		Args     []interface{}
		Expected interface{}
	}{
		{
			// Void function
			Fn: func() {},
		},
		{
			// Contexts and errors are allowed and handled.
			Fn: func(ctx context.Context) error { return nil },
		},
		{
			// Sum
			Fn:       func(a, b, c int) int { return a + b + c },
			Args:     []interface{}{1, 2, 3},
			Expected: 6,
		},
		{
			// Concat
			Fn:       func(a, b, c string) string { return a + b + c },
			Args:     []interface{}{"a", "b", "c"},
			Expected: "abc",
		},
		{
			// Length (slice type)
			Fn:       func(list []int) (int, error) { return len(list), nil },
			Args:     []interface{}{[]int{1, 2, 3}},
			Expected: 3,
		},
		{
			// Emitter
			Fn:   func(emit func(int)) { emit(1) },
			Args: []interface{}{func(int) {}},
		},
		{
			// Side input
			Fn: func(a int, get func(*int) bool) int {
				var ret int
				if !get(&ret) {
					return a
				}
				return ret
			},
			Args:     []interface{}{1, func(out *int) bool { *out = 2; return true }},
			Expected: 2,
		},
		{
			// Sum as Main
			Fn:       func(a, b, c int) int { return a + b + c },
			Opt:      &MainInput{Key: FullValue{Elm: 1}},
			Args:     []interface{}{2, 3},
			Expected: 6,
		},
		{
			// Sum as Main KV
			Fn:       func(a, b, c int) int { return a + b + c },
			Opt:      &MainInput{Key: FullValue{Elm: 1, Elm2: 2}},
			Args:     []interface{}{3},
			Expected: 6,
		},
		{
			// EventTime
			Fn:       func(ts typex.EventTime, a int) int { return int(ts.Milliseconds()) + a },
			Opt:      &MainInput{Key: FullValue{Elm: 1}},
			Expected: 3,
		},
		{
			// Window
			Fn:       func(w typex.Window, a int) int64 { return w.MaxTimestamp().Milliseconds() },
			Opt:      &MainInput{Key: FullValue{Elm: 1}},
			Expected: mtime.EndOfGlobalWindowTime.Milliseconds(),
		},
	}

	for _, test := range tests {
		fn, err := funcx.New(reflectx.MakeFunc(test.Fn))
		if err != nil {
			t.Fatalf("function not valid: %v", err)
		}

		ts := mtime.ZeroTimestamp.Add(2 * time.Millisecond)

		val, err := Invoke(context.Background(), window.SingleGlobalWindow, ts, fn, test.Opt, test.Args...)
		if err != nil {
			t.Fatalf("Invoke(%v,%v) failed: %v", fn.Fn.Name(), test.Args, err)
		}
		if val != nil && val.Elm != test.Expected {
			t.Errorf("Invoke(%v,%v) = %v, want %v", fn.Fn.Name(), test.Args, val.Elm, test.Expected)
		}
	}
}

// Benchmarks

// NOTE(herohde) 12/19/2017: example run on a laptop
//
// BenchmarkDirectCall-4              2000000000	         0.26 ns/op
// BenchmarkIndirectCall-4            1000000000	         2.28 ns/op
// BenchmarkReflectedAndBackCall-4    1000000000	         2.40 ns/op
// BenchmarkReflectCall-4            	10000000	       197    ns/op
// BenchmarkReflectCallNewArgs-4     	10000000	       200    ns/op
// BenchmarkReflectCallReuseArgs-4   	10000000	       196    ns/op
// BenchmarkInvokeCall-4             	 3000000	       452    ns/op
// BenchmarkInvokeCallExtra-4        	 3000000	       419    ns/op
// BenchmarkReflectFnCall-4          	 5000000	       340    ns/op
// BenchmarkInvokeFnCall-4           	 2000000	       604    ns/op
// BenchmarkInvokeFnCallExtra-4      	 3000000	       559    ns/op

func inc(n int) int {
	return n + 1
}

func BenchmarkDirectCall(b *testing.B) {
	n := 0
	for i := 0; i < b.N; i++ {
		n = inc(n)
	}
}

func BenchmarkIndirectCall(b *testing.B) {
	fn := func(int) int { return 0 }
	if b.N > 0 {
		fn = inc // this is always set, but the compiler doesn't know
	}

	n := 0
	for i := 0; i < b.N; i++ {
		n = fn(n)
	}
}

func BenchmarkReflectedAndBackCall(b *testing.B) {
	fn := reflect.ValueOf(inc).Interface().(func(int) int)
	n := 0
	for i := 0; i < b.N; i++ {
		n = fn(n)
	}
}

func BenchmarkReflectCall(b *testing.B) {
	fn := reflect.ValueOf(inc)
	n := reflect.ValueOf(0)
	for i := 0; i < b.N; i++ {
		n = fn.Call([]reflect.Value{n})[0]
	}
}

func BenchmarkReflectCallNewArgs(b *testing.B) {
	fn := reflect.ValueOf(inc)
	n := reflect.ValueOf(0)
	for i := 0; i < b.N; i++ {
		args := make([]reflect.Value, 1, 1)
		args[0] = n
		n = fn.Call(args)[0]
	}
}

func BenchmarkReflectCallReuseArgs(b *testing.B) {
	fn := reflect.ValueOf(inc)
	n := reflect.ValueOf(0)
	args := make([]reflect.Value, 1, 1)
	for i := 0; i < b.N; i++ {
		args[0] = n
		n = fn.Call(args)[0]
	}
}

func BenchmarkInvokeCall(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(inc))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, &MainInput{Key: FullValue{Elm: n}})
		n = ret.Elm.(int)
	}
}

func BenchmarkInvokeCallExtra(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(inc))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, nil, n)
		n = ret.Elm.(int)
	}
}

// The below take the additional overhead of MakeFunc.

func incFn(args []reflect.Value) []reflect.Value {
	return []reflect.Value{reflect.ValueOf(args[0].Interface().(int) + 1)}
}

func BenchmarkReflectFnCall(b *testing.B) {
	fn := reflect.MakeFunc(reflect.TypeOf(inc), incFn)
	n := reflect.ValueOf(0)
	for i := 0; i < b.N; i++ {
		n = fn.Call([]reflect.Value{n})[0]
	}
}

func BenchmarkInvokeFnCall(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(reflect.MakeFunc(reflect.TypeOf(inc), incFn).Interface().(func(int) int)))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, &MainInput{Key: FullValue{Elm: n}})
		n = ret.Elm.(int)
	}
}

func BenchmarkInvokeFnCallExtra(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(reflect.MakeFunc(reflect.TypeOf(inc), incFn).Interface().(func(int) int)))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, nil, n)
		n = ret.Elm.(int)
	}
}
<commit_msg>Update fn_test.go<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package exec

import (
	"context"
	"reflect"
	"testing"
	"time"

	"github.com/apache/beam/sdks/go/pkg/beam/core/funcx"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/mtime"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/window"
	"github.com/apache/beam/sdks/go/pkg/beam/core/typex"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/reflectx"
)

// TestInvoke verifies the the various forms of input to Invoke are handled correctly.
func TestInvoke(t *testing.T) {
	tests := []struct {
		Fn       interface{}
		Opt      *MainInput
		Args     []interface{}
		Expected interface{}
	}{
		{
			// Void function
			Fn: func() {},
		},
		{
			// Contexts and errors are allowed and handled.
			Fn: func(ctx context.Context) error { return nil },
		},
		{
			// Sum
			Fn:       func(a, b, c int) int { return a + b + c },
			Args:     []interface{}{1, 2, 3},
			Expected: 6,
		},
		{
			// Concat
			Fn:       func(a, b, c string) string { return a + b + c },
			Args:     []interface{}{"a", "b", "c"},
			Expected: "abc",
		},
		{
			// Length (slice type)
			Fn:       func(list []int) (int, error) { return len(list), nil },
			Args:     []interface{}{[]int{1, 2, 3}},
			Expected: 3,
		},
		{
			// Emitter
			Fn:   func(emit func(int)) { emit(1) },
			Args: []interface{}{func(int) {}},
		},
		{
			// Side input
			Fn: func(a int, get func(*int) bool) int {
				var ret int
				if !get(&ret) {
					return a
				}
				return ret
			},
			Args:     []interface{}{1, func(out *int) bool { *out = 2; return true }},
			Expected: 2,
		},
		{
			// Sum as Main
			Fn:       func(a, b, c int) int { return a + b + c },
			Opt:      &MainInput{Key: FullValue{Elm: 1}},
			Args:     []interface{}{2, 3},
			Expected: 6,
		},
		{
			// Sum as Main KV
			Fn:       func(a, b, c int) int { return a + b + c },
			Opt:      &MainInput{Key: FullValue{Elm: 1, Elm2: 2}},
			Args:     []interface{}{3},
			Expected: 6,
		},
		{
			// EventTime
			Fn:       func(ts typex.EventTime, a int) int { return int(ts.Milliseconds()) + a },
			Opt:      &MainInput{Key: FullValue{Elm: 1}},
			Expected: 3,
		},
		{
			// Window
			Fn:       func(w typex.Window, a int) int64 { return w.MaxTimestamp().Milliseconds() },
			Opt:      &MainInput{Key: FullValue{Elm: 1}},
			Expected: mtime.EndOfGlobalWindowTime.Milliseconds(),
		},
	}

	for _, test := range tests {
		fn, err := funcx.New(reflectx.MakeFunc(test.Fn))
		if err != nil {
			t.Fatalf("function not valid: %v", err)
		}

		ts := mtime.ZeroTimestamp.Add(2 * time.Millisecond)

		val, err := Invoke(context.Background(), window.SingleGlobalWindow, ts, fn, test.Opt, test.Args...)
		if err != nil {
			t.Fatalf("Invoke(%v,%v) failed: %v", fn.Fn.Name(), test.Args, err)
		}
		if val != nil && val.Elm != test.Expected {
			t.Errorf("Invoke(%v,%v) = %v, want %v", fn.Fn.Name(), test.Args, val.Elm, test.Expected)
		}
	}
}

// Benchmarks

// NOTE(herohde) 12/19/2017: example run on a laptop
//
// BenchmarkDirectCall-4              2000000000	         0.26 ns/op
// BenchmarkIndirectCall-4            1000000000	         2.28 ns/op
// BenchmarkReflectedAndBackCall-4    1000000000	         2.40 ns/op
// BenchmarkReflectCall-4            	10000000	       197    ns/op
// BenchmarkReflectCallNewArgs-4     	10000000	       200    ns/op
// BenchmarkReflectCallReuseArgs-4   	10000000	       196    ns/op
// BenchmarkInvokeCall-4             	 3000000	       452    ns/op
// BenchmarkInvokeCallExtra-4        	 3000000	       419    ns/op
// BenchmarkReflectFnCall-4          	 5000000	       340    ns/op
// BenchmarkInvokeFnCall-4           	 2000000	       604    ns/op
// BenchmarkInvokeFnCallExtra-4      	 3000000	       559    ns/op

func inc(n int) int {
	return n + 1
}

func BenchmarkDirectCall(b *testing.B) {
	n := 0
	for i := 0; i < b.N; i++ {
		n = inc(n)
	}
	b.Log(n)
}

func BenchmarkIndirectCall(b *testing.B) {
	fn := func(int) int { return 0 }
	if b.N > 0 {
		fn = inc // this is always set, but the compiler doesn't know
	}

	n := 0
	for i := 0; i < b.N; i++ {
		n = fn(n)
	}
	b.Log(n)
}

func BenchmarkReflectedAndBackCall(b *testing.B) {
	fn := reflect.ValueOf(inc).Interface().(func(int) int)
	n := 0
	for i := 0; i < b.N; i++ {
		n = fn(n)
	}
	b.Log(n)
}

func BenchmarkReflectCall(b *testing.B) {
	fn := reflect.ValueOf(inc)
	n := reflect.ValueOf(0)
	for i := 0; i < b.N; i++ {
		n = fn.Call([]reflect.Value{n})[0]
	}
	b.Log(n)
}

func BenchmarkReflectCallNewArgs(b *testing.B) {
	fn := reflect.ValueOf(inc)
	n := reflect.ValueOf(0)
	for i := 0; i < b.N; i++ {
		args := make([]reflect.Value, 1, 1)
		args[0] = n
		n = fn.Call(args)[0]
	}
	b.Log(n)
}

func BenchmarkReflectCallReuseArgs(b *testing.B) {
	fn := reflect.ValueOf(inc)
	n := reflect.ValueOf(0)
	args := make([]reflect.Value, 1, 1)
	for i := 0; i < b.N; i++ {
		args[0] = n
		n = fn.Call(args)[0]
	}
	b.Log(n)
}

func BenchmarkInvokeCall(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(inc))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, &MainInput{Key: FullValue{Elm: n}})
		n = ret.Elm.(int)
	}
	b.Log(n)
}

func BenchmarkInvokeCallExtra(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(inc))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, nil, n)
		n = ret.Elm.(int)
	}
	b.Log(n)
}

// The below take the additional overhead of MakeFunc.

func incFn(args []reflect.Value) []reflect.Value {
	return []reflect.Value{reflect.ValueOf(args[0].Interface().(int) + 1)}
}

func BenchmarkReflectFnCall(b *testing.B) {
	fn := reflect.MakeFunc(reflect.TypeOf(inc), incFn)
	n := reflect.ValueOf(0)
	for i := 0; i < b.N; i++ {
		n = fn.Call([]reflect.Value{n})[0]
	}
	b.Log(n)
}

func BenchmarkInvokeFnCall(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(reflect.MakeFunc(reflect.TypeOf(inc), incFn).Interface().(func(int) int)))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, &MainInput{Key: FullValue{Elm: n}})
		n = ret.Elm.(int)
	}
	b.Log(n)
}

func BenchmarkInvokeFnCallExtra(b *testing.B) {
	fn, _ := funcx.New(reflectx.MakeFunc(reflect.MakeFunc(reflect.TypeOf(inc), incFn).Interface().(func(int) int)))
	ctx := context.Background()
	n := 0
	for i := 0; i < b.N; i++ {
		ret, _ := InvokeWithoutEventTime(ctx, fn, nil, n)
		n = ret.Elm.(int)
	}
	b.Log(n)
}
<|endoftext|>


<commit_before>package main

import (
	"strings"

	"github.com/codegangsta/cli"
	sdk "github.com/dysolution/espsdk"
)

var batchTypes = string(strings.Join(sdk.Batch{}.ValidTypes(), " OR "))

// A Batch wraps the verbs provided by the ESP API for Submission Batches.
type Batch struct{ context *cli.Context }

// Index requests a list of all Submission Batches belonging to the user.
func (b Batch) Index() sdk.BatchListContainer { return sdk.Batch{}.Index(&client) }

// Get requests the metadata for a specific Submission Batch.
func (b Batch) Get() sdk.DeserializedObject { return sdk.Get(b.path(), &client) }

// Create adds a new Submission Batch.
func (b Batch) Create() sdk.Createable {
	data := b.build()
	return sdk.Create(data.Path(), data, &client)
}

// Update changes fields for an existing Submission Batch.
func (b Batch) Update() sdk.Createable { return batch(b.id()).Update(&client, b.buildUpdate()) }

// Delete destroys a specific Submission Batch.
func (b Batch) Delete() { client.Delete(b.path()) }

// Unmarshal attempts to deserialize the provided JSON payload into a SubmissionBatch object.
func (b Batch) Unmarshal(payload []byte) sdk.DeserializedObject {
	return sdk.Unmarshal(payload)
}

func (b Batch) id() int      { return getBatchID(b.context) }
func (b Batch) path() string { return sdk.Batch{ID: b.id()}.Path() }

func (b Batch) build() sdk.Batch {
	return sdk.Batch{
		BriefID:               b.context.String("brief-id"),
		EventID:               b.context.String("event-id"),
		Note:                  b.context.String("note"),
		AssignmentID:          b.context.String("assignment-id"),
		SaveExtractedMetadata: b.context.Bool("save-extracted-metadata"),
		SubmissionName:        b.context.String("submission-name"),
		SubmissionType:        b.context.String("submission-type"),
	}
}

func (b Batch) buildUpdate() sdk.BatchUpdate {
	return sdk.BatchUpdate{b.build()}
}
<commit_msg>use client.Get()<commit_after>package main

import (
	"strings"

	"github.com/codegangsta/cli"
	sdk "github.com/dysolution/espsdk"
)

var batchTypes = string(strings.Join(sdk.Batch{}.ValidTypes(), " OR "))

// A Batch wraps the verbs provided by the ESP API for Submission Batches.
type Batch struct{ context *cli.Context }

// Index requests a list of all Submission Batches belonging to the user.
func (b Batch) Index() sdk.BatchListContainer { return sdk.Batch{}.Index(&client) }

// Get requests the metadata for a specific Submission Batch.
func (b Batch) Get() sdk.DeserializedObject { return client.Get(b.path()) }

// Create adds a new Submission Batch.
func (b Batch) Create() sdk.Createable {
	data := b.build()
	return sdk.Create(data.Path(), data, &client)
}

// Update changes fields for an existing Submission Batch.
func (b Batch) Update() sdk.Createable { return batch(b.id()).Update(&client, b.buildUpdate()) }

// Delete destroys a specific Submission Batch.
func (b Batch) Delete() { client.Delete(b.path()) }

// Unmarshal attempts to deserialize the provided JSON payload into a SubmissionBatch object.
func (b Batch) Unmarshal(payload []byte) sdk.DeserializedObject {
	return sdk.Unmarshal(payload)
}

func (b Batch) id() int      { return getBatchID(b.context) }
func (b Batch) path() string { return sdk.Batch{ID: b.id()}.Path() }

func (b Batch) build() sdk.Batch {
	return sdk.Batch{
		BriefID:               b.context.String("brief-id"),
		EventID:               b.context.String("event-id"),
		Note:                  b.context.String("note"),
		AssignmentID:          b.context.String("assignment-id"),
		SaveExtractedMetadata: b.context.Bool("save-extracted-metadata"),
		SubmissionName:        b.context.String("submission-name"),
		SubmissionType:        b.context.String("submission-type"),
	}
}

func (b Batch) buildUpdate() sdk.BatchUpdate {
	return sdk.BatchUpdate{b.build()}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Pagoda Box Inc
//
// 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/.
//

package monitor

import (
	"bufio"
	"database/sql"
	"errors"
	"fmt"
	"github.com/hoisie/mustache"
	_ "github.com/lib/pq"
	"github.com/nanobox-io/yoke/config"
	"github.com/nanobox-io/yoke/state"
	"io"
	"net"
	"os"
	"os/exec"
	"sync"
	"syscall"
	"time"
)

var (
	Done = errors.New("done")
)

type (
	Performer interface {
		TransitionToActive()
		TransitionToBackup()
		TransitionToSingle()
		Stop()
		Loop() error
	}

	performer struct {
		sync.Mutex
		step  map[string]bool
		me    state.State
		other state.State
		err   chan error
		cmd   *exec.Cmd
	}
)

func NewPrefix(prefix string) io.Writer {
	read, write := io.Pipe()
	scan := bufio.NewScanner(read)
	go func() {
		for scan.Scan() {
			fmt.Printf("%v %v\n", prefix, scan.Text())
		}
	}()
	return write
}

func NewPerformer(me state.State, other state.State) *performer {
	perform := performer{
		step: map[string]bool{
			"trigger": true, // this should only be there if the trigger file exists
		},
		me:    me,
		other: other,
		err:   make(chan error),
	}

	return &perform
}

func (performer *performer) Loop() error {
	config.Log.Info("Going to Init")
	if err := performer.Initialize(); err != nil {
		return err
	}
	config.Log.Info("Waiting for error")
	return <-performer.err
}

func (performer *performer) Stop() {
	config.Log.Info("going to stop")
	performer.Lock()
	defer performer.Unlock()
	config.Log.Info("stopping")

	performer.stop()
	performer.err <- nil
}

func (performer *performer) TransitionToSingle() {
	performer.Lock()
	defer performer.Unlock()

	// backups or actives can transition to single
	// it just means that the other node went down
	role, err := performer.me.GetDBRole()
	if err != nil {
		performer.err <- err
		return
	}
	if role == "single" {
		return
	}
	performer.me.SetDBRole("single")

	err = performer.Single()
	if err != nil {
		performer.err <- err
	}
}

func (performer *performer) TransitionToActive() {
	performer.Lock()
	defer performer.Unlock()

	role, err := performer.me.GetDBRole()
	if err != nil {
		performer.err <- err
		return
	}
	switch role {
	case "active":
		return
	case "backup":
		// backups must transition to single before they can become active.
		panic("something went seriously wrong, backups cannot transition to active.")
	}

	err = performer.Active()
	if err != nil {
		performer.err <- err
	}
}

func (performer *performer) TransitionToBackup() {
	performer.Lock()
	defer performer.Unlock()

	role, err := performer.me.GetDBRole()
	if err != nil {
		performer.err <- err
		return
	}
	if role == "backup" {
		return
	}

	err = performer.Backup()
	if err != nil {
		performer.err <- err
	}
}

func (performer *performer) stop() {
	if !performer.step["started"] {
		// do any steps needed to stop postgres
		performer.step["started"] = false
	}
}

func (performer *performer) Initialize() error {
	_, err := os.Stat(config.Conf.DataDir)
	switch {
	case os.IsNotExist(err):
		config.Log.Info("creating database")
		init := exec.Command("initdb", config.Conf.DataDir)
		init.Stdout = NewPrefix("[initdb.stdout]")
		init.Stderr = NewPrefix("[initdb.stderr]")
		if err = init.Run(); err != nil {
			return err
		}
	default:
		config.Log.Info("database has already been created... skipping.")
	}
	return err
}

// The Single state.
func (performer *performer) Single() error {
	config.Log.Info("transitioning to Single")

	// disable syncronus transaction commits.
	if err := performer.setSync(false, nil); err != nil {
		return err
	}

	if err := performer.replicate(false); err != nil {
		return err
	}

	config.Log.Info("[action] running DB as single")

	addVip()
	roleChangeCommand("single")

	return nil
}

func (performer *performer) sync(command string) error {
	sc := exec.Command("bash", "-c", command)
	sc.Stdout = NewPrefix("[pre-sync.stdout]")
	sc.Stderr = NewPrefix("[pre-sync.stderr]")
	config.Log.Info("[action] running pre-sync")
	config.Log.Debug("[action] pre-sync command(%s)", command)

	return sc.Run()
}

func (performer *performer) replicate(enabled bool) error {
	if performer.step["trigger"] == enabled {
		return nil
	}
	performer.step["trigger"] = enabled

	trigger := config.Conf.StatusDir + "/i-am-primary"
	switch enabled {
	case true:
		return os.Remove(trigger)
	default:
		// this is a trigger file, it should stop this node from replicating from a
		// remote node,
		f, err := os.Create(trigger)
		if err != nil {
			return err
		}
		f.Close()
		return nil
	}
}

func pgConnect() (*sql.DB, error) {
	return sql.Open("postgres", fmt.Sprintf("user=%s database=postgres sslmode=disable host=localhost port=%d", config.Conf.SystemUser, config.Conf.PGPort))
}

func (performer *performer) setSync(enabled bool, db *sql.DB) error {
	if db == nil {
		db, err := pgConnect()
		if err != nil {
			return err
		}
		defer db.Close()
	}
	var sync string
	switch enabled {
	case true:
		sync = "on"
	default:
		sync = "off"
	}
	_, err := db.Exec(fmt.Sprintf("ALTER USER %v SET synchronous_commit=%v", config.Conf.SystemUser, sync))
	return err

}

// The Active state.
func (performer *performer) Active() error {
	config.Log.Info("transitioning to Active")
	if err := performer.replicate(false); err != nil {
		return err
	}

	performer.startDB()

	// do an initial copy of files which might be corrupt because they are not consistant
	// this will be fixed later. we do this now so that a majority of the data will make it across without
	// having to pause the Durablility (ACID compliance) of postgres
	config.Log.Debug("[action] pre-backup started")
	dataDir, err := performer.other.GetDataDir()
	if err != nil {
		return err
	}
	location := performer.other.Location()
	ip, _, err := net.SplitHostPort(location)
	if err != nil {
		return err
	}
	sync := mustache.Render(config.Conf.SyncCommand, map[string]string{"local_dir": config.Conf.DataDir, "slave_ip": ip, "slave_dir": dataDir})

	if err := performer.sync(sync); err != nil {
		return err
	}

	db, err := pgConnect()
	if err != nil {
		return err
	}
	defer db.Close()

	// this informs postgres to make the files on disk consistant for copying,
	// all changes are kept in memory from this point on
	_, err = db.Exec("select pg_start_backup('replication')")
	if err != nil {
		return err
	}

	config.Log.Debug("[action] backup started")

	if err := performer.sync(sync); err != nil {
		// stop the backup, if it fails, there is nothing we can do so we return the original error
		db.Exec("select pg_stop_backup()")

		// something went wrong, we are the master still, so lets wait for the slave to reconnect
		return nil
	}

	// connect to DB and tell it to stop backup
	if _, err = db.Exec("select pg_stop_backup()"); err != nil {
		return err
	}

	config.Log.Debug("[action] backup complete")

	// if we were unsucessfull at setting the sync flag on the other node
	// then we need to start all over
	if performer.other.SetSynced(true) != nil {
		return nil
	}

	// enable syncronus transaction commits.
	if err := performer.setSync(true, db); err != nil {
		return err
	}

	addVip()
	roleChangeCommand("master")

	performer.me.SetDBRole("active")
	return nil
}

// The Backup state.
func (performer *performer) Backup() error {
	config.Log.Info("transitioning to Backup")
	removeVip()

	// TODO figure out if the recover.conf file needs to be regenerated.

	// wait for master server to be running
	for {
		ready, err := performer.me.HasSynced()
		if err != nil {
			return err
		}
		if ready {
			break
		}
		time.Sleep(time.Second)
	}

	config.Log.Debug("[action] starting database")
	performer.startDB()
	roleChangeCommand("backup")
	return performer.me.SetDBRole("backup")
}

// this will kill the database that is running. reguardless of its current state
func (performer *performer) killDB() {
	config.Log.Debug("[action] KillingDB")

	if performer.cmd == nil {
		config.Log.Debug("[action] nothing to kill")
		return
	}

	err := performer.cmd.Process.Signal(syscall.SIGINT)
	if err != nil {
		config.Log.Error("[action] Kill Signal error: %s", err.Error())
	}
}

func (performer *performer) startDB() {
	config.Log.Debug("[action] starting db")
	cmd := exec.Command("postgres", "-D", config.Conf.DataDir)
	cmd.Stdout = NewPrefix("[postgres.stdout]")
	cmd.Stderr = NewPrefix("[postgres.stderr]")
	cmd.Start()
	performer.cmd = cmd
	go performer.waitForExit()
	config.Log.Debug("[action] db started")
}

func (performer *performer) waitForExit() {
	err := performer.cmd.Wait()
	if err != nil {
		performer.err <- err
	}
}

func roleChangeCommand(role string) {
	if config.Conf.RoleChangeCommand != "" {
		rcc := exec.Command("bash", "-c", fmt.Sprintf("%s %s", config.Conf.RoleChangeCommand, role))
		rcc.Stdout = NewPrefix("[RoleChangeCommand.stdout]")
		rcc.Stderr = NewPrefix("[RoleChangeCommand.stderr]")
		if err := rcc.Run(); err != nil {
			config.Log.Error("[action] RoleChangeCommand failed.")
			config.Log.Debug("[RoleChangeCommand.error] message: %s", err.Error())
		}
	}
}

func addVip() {
	if vipable() {
		config.Log.Info("[action] Adding VIP")
		vAddCmd := exec.Command("bash", "-c", fmt.Sprintf("%s %s", config.Conf.VipAddCommand, config.Conf.Vip))
		vAddCmd.Stdout = NewPrefix("[VIPAddCommand.stdout]")
		vAddCmd.Stderr = NewPrefix("[VIPAddCommand.stderr]")
		if err := vAddCmd.Run(); err != nil {
			config.Log.Error("[action] VIPAddCommand failed.")
			config.Log.Debug("[VIPAddCommand.error] message: %s", err.Error())
		}
	}
}

func removeVip() {
	if vipable() {
		config.Log.Info("[action] Removing VIP")
		vRemoveCmd := exec.Command("bash", "-c", fmt.Sprintf("%s %s", config.Conf.VipRemoveCommand, config.Conf.Vip))
		vRemoveCmd.Stdout = NewPrefix("[VIPRemoveCommand.stdout]")
		vRemoveCmd.Stderr = NewPrefix("[VIPRemoveCommand.stderr]")
		if err := vRemoveCmd.Run(); err != nil {
			config.Log.Error("[action] VIPRemoveCommand failed.")
			config.Log.Debug("[VIPRemoveCommand.error] message: %s", err.Error())
		}
	}
}

func vipable() bool {
	return config.Conf.Vip != "" && config.Conf.VipAddCommand != "" && config.Conf.VipRemoveCommand != ""
}
<commit_msg>added more logging<commit_after>// Copyright (c) 2015 Pagoda Box Inc
//
// 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/.
//

package monitor

import (
	"bufio"
	"database/sql"
	"errors"
	"fmt"
	"github.com/hoisie/mustache"
	_ "github.com/lib/pq"
	"github.com/nanobox-io/yoke/config"
	"github.com/nanobox-io/yoke/state"
	"io"
	"net"
	"os"
	"os/exec"
	"sync"
	"syscall"
	"time"
)

var (
	Done = errors.New("done")
)

type (
	Performer interface {
		TransitionToActive()
		TransitionToBackup()
		TransitionToSingle()
		Stop()
		Loop() error
	}

	performer struct {
		sync.Mutex
		step  map[string]bool
		me    state.State
		other state.State
		err   chan error
		cmd   *exec.Cmd
	}
)

func NewPrefix(prefix string) io.Writer {
	read, write := io.Pipe()
	scan := bufio.NewScanner(read)
	go func() {
		for scan.Scan() {
			fmt.Printf("%v %v\n", prefix, scan.Text())
		}
	}()
	return write
}

func NewPerformer(me state.State, other state.State) *performer {
	perform := performer{
		step: map[string]bool{
			"trigger": true, // this should only be there if the trigger file exists
		},
		me:    me,
		other: other,
		err:   make(chan error),
	}

	return &perform
}

func (performer *performer) Loop() error {
	config.Log.Info("Going to Init")
	if err := performer.Initialize(); err != nil {
		return err
	}
	config.Log.Info("Waiting for error")
	return <-performer.err
}

func (performer *performer) Stop() {
	config.Log.Info("going to stop")
	performer.Lock()
	defer performer.Unlock()
	config.Log.Info("stopping")

	performer.stop()
	performer.err <- nil
}

func (performer *performer) TransitionToSingle() {
	performer.Lock()
	defer performer.Unlock()

	// backups or actives can transition to single
	// it just means that the other node went down
	role, err := performer.me.GetDBRole()
	if err != nil {
		performer.err <- err
		return
	}
	if role == "single" {
		return
	}
	performer.me.SetDBRole("single")

	err = performer.Single()
	if err != nil {
		performer.err <- err
	}
}

func (performer *performer) TransitionToActive() {
	performer.Lock()
	defer performer.Unlock()

	role, err := performer.me.GetDBRole()
	if err != nil {
		performer.err <- err
		return
	}
	switch role {
	case "active":
		return
	case "backup":
		// backups must transition to single before they can become active.
		panic("something went seriously wrong, backups cannot transition to active.")
	}

	err = performer.Active()
	if err != nil {
		performer.err <- err
	}
}

func (performer *performer) TransitionToBackup() {
	performer.Lock()
	defer performer.Unlock()

	role, err := performer.me.GetDBRole()
	if err != nil {
		performer.err <- err
		return
	}
	if role == "backup" {
		return
	}

	err = performer.Backup()
	if err != nil {
		performer.err <- err
	}
}

func (performer *performer) stop() {
	if !performer.step["started"] {
		// do any steps needed to stop postgres
		performer.step["started"] = false
	}
}

func (performer *performer) Initialize() error {
	_, err := os.Stat(config.Conf.DataDir)
	switch {
	case os.IsNotExist(err):
		config.Log.Info("creating database")
		init := exec.Command("initdb", config.Conf.DataDir)
		init.Stdout = NewPrefix("[initdb.stdout]")
		init.Stderr = NewPrefix("[initdb.stderr]")
		if err = init.Run(); err != nil {
			return err
		}
	default:
		config.Log.Info("database has already been created... skipping.")
	}
	return err
}

// The Single state.
func (performer *performer) Single() error {
	config.Log.Info("transitioning to Single")

	// disable syncronus transaction commits.
	if err := performer.setSync(false, nil); err != nil {
		return err
	}

	if err := performer.replicate(false); err != nil {
		return err
	}

	config.Log.Info("[action] running DB as single")

	addVip()
	roleChangeCommand("single")

	return nil
}

func (performer *performer) sync(command string) error {
	sc := exec.Command("bash", "-c", command)
	sc.Stdout = NewPrefix("[pre-sync.stdout]")
	sc.Stderr = NewPrefix("[pre-sync.stderr]")
	config.Log.Info("[action] running pre-sync")
	config.Log.Debug("[action] pre-sync command(%s)", command)

	return sc.Run()
}

func (performer *performer) replicate(enabled bool) error {
	if performer.step["trigger"] == enabled {
		return nil
	}
	performer.step["trigger"] = enabled

	trigger := config.Conf.StatusDir + "/i-am-primary"
	switch enabled {
	case true:
		return os.Remove(trigger)
	default:
		// this is a trigger file, it should stop this node from replicating from a
		// remote node,
		f, err := os.Create(trigger)
		if err != nil {
			return err
		}
		f.Close()
		return nil
	}
}

func pgConnect() (*sql.DB, error) {
	return sql.Open("postgres", fmt.Sprintf("user=%s database=postgres sslmode=disable host=localhost port=%d", config.Conf.SystemUser, config.Conf.PGPort))
}

func (performer *performer) setSync(enabled bool, db *sql.DB) error {
	if db == nil {
		db, err := pgConnect()
		if err != nil {
			return err
		}
		defer db.Close()
	}
	var sync string
	switch enabled {
	case true:
		sync = "on"
	default:
		sync = "off"
	}
	_, err := db.Exec(fmt.Sprintf("ALTER USER %v SET synchronous_commit=%v", config.Conf.SystemUser, sync))
	return err

}

// The Active state.
func (performer *performer) Active() error {
	config.Log.Info("transitioning to Active")
	if err := performer.replicate(false); err != nil {
		return err
	}

	performer.startDB()

	// do an initial copy of files which might be corrupt because they are not consistant
	// this will be fixed later. we do this now so that a majority of the data will make it across without
	// having to pause the Durablility (ACID compliance) of postgres
	config.Log.Debug("[action] pre-backup started")
	dataDir, err := performer.other.GetDataDir()
	if err != nil {
		return err
	}
	location := performer.other.Location()
	ip, _, err := net.SplitHostPort(location)
	if err != nil {
		return err
	}
	sync := mustache.Render(config.Conf.SyncCommand, map[string]string{"local_dir": config.Conf.DataDir, "slave_ip": ip, "slave_dir": dataDir})

	if err := performer.sync(sync); err != nil {
		return err
	}

	db, err := pgConnect()
	if err != nil {
		return err
	}
	defer db.Close()

	// this informs postgres to make the files on disk consistant for copying,
	// all changes are kept in memory from this point on
	_, err = db.Exec("select pg_start_backup('replication')")
	if err != nil {
		return err
	}

	config.Log.Debug("[action] backup started")

	if err := performer.sync(sync); err != nil {
		// stop the backup, if it fails, there is nothing we can do so we return the original error
		db.Exec("select pg_stop_backup()")

		// something went wrong, we are the master still, so lets wait for the slave to reconnect
		return nil
	}

	// connect to DB and tell it to stop backup
	if _, err = db.Exec("select pg_stop_backup()"); err != nil {
		return err
	}

	config.Log.Debug("[action] backup complete")

	// if we were unsucessfull at setting the sync flag on the other node
	// then we need to start all over
	if performer.other.SetSynced(true) != nil {
		return nil
	}

	// enable syncronus transaction commits.
	if err := performer.setSync(true, db); err != nil {
		return err
	}

	addVip()
	roleChangeCommand("master")

	performer.me.SetDBRole("active")
	return nil
}

// The Backup state.
func (performer *performer) Backup() error {
	config.Log.Info("transitioning to Backup")
	removeVip()

	// TODO figure out if the recover.conf file needs to be regenerated.

	// wait for master server to be running
	for {
		ready, err := performer.me.HasSynced()
		if err != nil {
			return err
		}
		if ready {
			break
		}
		time.Sleep(time.Second)
	}

	config.Log.Debug("[action] starting database")
	performer.startDB()
	roleChangeCommand("backup")
	return performer.me.SetDBRole("backup")
}

// this will kill the database that is running. reguardless of its current state
func (performer *performer) killDB() {
	config.Log.Debug("[action] KillingDB")

	if performer.cmd == nil {
		config.Log.Debug("[action] nothing to kill")
		return
	}

	err := performer.cmd.Process.Signal(syscall.SIGINT)
	if err != nil {
		config.Log.Error("[action] Kill Signal error: %s", err.Error())
	}
}

func (performer *performer) startDB() {
	config.Log.Info("[action] starting db")
	cmd := exec.Command("postgres", "-D", config.Conf.DataDir)
	cmd.Stdout = NewPrefix("[postgres.stdout]")
	cmd.Stderr = NewPrefix("[postgres.stderr]")
	cmd.Start()
	performer.cmd = cmd
	go performer.waitForExit()
	config.Log.Info("[action] db started")
}

func (performer *performer) waitForExit() {
	err := performer.cmd.Wait()
	if err != nil {
		performer.err <- err
	}
}

func roleChangeCommand(role string) {
	if config.Conf.RoleChangeCommand != "" {
		rcc := exec.Command("bash", "-c", fmt.Sprintf("%s %s", config.Conf.RoleChangeCommand, role))
		rcc.Stdout = NewPrefix("[RoleChangeCommand.stdout]")
		rcc.Stderr = NewPrefix("[RoleChangeCommand.stderr]")
		if err := rcc.Run(); err != nil {
			config.Log.Error("[action] RoleChangeCommand failed.")
			config.Log.Debug("[RoleChangeCommand.error] message: %s", err.Error())
		}
	}
}

func addVip() {
	if vipable() {
		config.Log.Info("[action] Adding VIP")
		vAddCmd := exec.Command("bash", "-c", fmt.Sprintf("%s %s", config.Conf.VipAddCommand, config.Conf.Vip))
		vAddCmd.Stdout = NewPrefix("[VIPAddCommand.stdout]")
		vAddCmd.Stderr = NewPrefix("[VIPAddCommand.stderr]")
		if err := vAddCmd.Run(); err != nil {
			config.Log.Error("[action] VIPAddCommand failed.")
			config.Log.Debug("[VIPAddCommand.error] message: %s", err.Error())
		}
	}
}

func removeVip() {
	if vipable() {
		config.Log.Info("[action] Removing VIP")
		vRemoveCmd := exec.Command("bash", "-c", fmt.Sprintf("%s %s", config.Conf.VipRemoveCommand, config.Conf.Vip))
		vRemoveCmd.Stdout = NewPrefix("[VIPRemoveCommand.stdout]")
		vRemoveCmd.Stderr = NewPrefix("[VIPRemoveCommand.stderr]")
		if err := vRemoveCmd.Run(); err != nil {
			config.Log.Error("[action] VIPRemoveCommand failed.")
			config.Log.Debug("[VIPRemoveCommand.error] message: %s", err.Error())
		}
	}
}

func vipable() bool {
	return config.Conf.Vip != "" && config.Conf.VipAddCommand != "" && config.Conf.VipRemoveCommand != ""
}
<|endoftext|>