<commit_before>package main

import (
	"fmt"
	"os"
	"net/http"
	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/spf13/viper"
	"path"
)

var (
	hostFlag = kingpin.Flag("host", "Set the host of Hoverfly").String()
	adminPortFlag = kingpin.Flag("admin-port", "Set the admin port of Hoverfly").String()
	proxyPortFlag = kingpin.Flag("proxy-port", "Set the admin port of Hoverfly").String()

	modeCommand = kingpin.Command("mode", "Get Hoverfly's current mode")
	modeNameArg = modeCommand.Arg("name", "Set Hoverfly's mode").String()

	startCommand = kingpin.Command("start", "Start a local instance of Hoverfly")
	stopCommand = kingpin.Command("stop", "Stop a local instance of Hoverfly")

	exportCommand = kingpin.Command("export", "Exports data out of Hoverfly")
	exportNameArg = exportCommand.Arg("name", "Name of exported simulation").Required().String()

	importCommand = kingpin.Command("import", "Imports data into Hoverfly")
	importNameArg = importCommand.Arg("name", "Name of imported simulation").Required().String()

	pushCommand = kingpin.Command("push", "Pushes the data to Specto Hub")
	pushNameArg = pushCommand.Arg("name", "Name of exported simulation").Required().String()

	pullCommand = kingpin.Command("pull", "Pushes the data to Specto Hub")
	pullNameArg = pullCommand.Arg("name", "Name of imported simulation").Required().String()
	pullOverrideHostFlag = pullCommand.Flag("override-host", "Name of the host you want to virtualise").String()

	wipeCommand = kingpin.Command("wipe", "Wipe Hoverfly database")
)

func main() {
	setConfigurationDefaults()

	viper.ReadInConfig()
	configUri := viper.ConfigFileUsed()

	hoverflyDirectory := getHoverflyDirectory(configUri)

	cacheDirectory, err := createCacheDirectory(hoverflyDirectory)
	if err != nil {
		failAndExit(err)
	}

	localCache := LocalCache{
		Uri: cacheDirectory,
	}

	kingpin.Parse()

	hoverfly := createHoverfly(*hostFlag, *adminPortFlag, *proxyPortFlag)

	spectoLab := SpectoLab{
		Host: viper.GetString("specto.lab.host"),
		Port: viper.GetString("specto.lab.port"),
		ApiKey: viper.GetString("specto.lab.api.key"),
	}

	switch kingpin.Parse() {
		case modeCommand.FullCommand():
			if *modeNameArg == "" || *modeNameArg == "status"{

				mode, err := hoverfly.GetMode()
				if err == nil {
					fmt.Println("Hoverfly is set to", mode, "mode")
				} else {
					failAndExit(err)
				}

			} else {

				mode, err := hoverfly.SetMode(*modeNameArg)
				if err == nil {
					fmt.Println("Hoverfly has been set to", mode, "mode")
				} else {
					failAndExit(err)
				}

			}

		case startCommand.FullCommand():
			err := startHandler(hoverflyDirectory, hoverfly)
			if err != nil {
				failAndExit(err)
			}

		case stopCommand.FullCommand():
			stopHandler(hoverflyDirectory, hoverfly)

		case exportCommand.FullCommand():
			hoverfile, err := NewHoverfile(*exportNameArg)
			if err != nil {
				failAndExit(err)
			}

			exportedData, err := hoverfly.ExportSimulation()

			if err != nil {
				failAndExit(err)
			}

			if err = localCache.WriteSimulation(hoverfile, exportedData); err == nil {
				fmt.Println(*exportNameArg, "exported successfully")
			} else {
				failAndExit(err)
			}

		case importCommand.FullCommand():
			hoverfile, err := NewHoverfile(*importNameArg)
			if err != nil {
				failAndExit(err)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExit(err)
			}

			if err = hoverfly.ImportSimulation(string(data)); err == nil {
				fmt.Println(hoverfile.String(), "imported successfully")
			} else {
				failAndExit(err)
			}

		case pushCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pushNameArg)
			if err != nil {
				failAndExit(err)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExit(err)
			}

			statusCode, err := spectoLab.UploadSimulation(hoverfile, data)
			if err != nil {
				failAndExit(err)
			}

			if statusCode == 200 {
				fmt.Println(hoverfile.String(), "has been pushed to the Specto Lab")
			}

		case pullCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pullNameArg)
			if err != nil {
				failAndExit(err)
			}

			data := spectoLab.GetSimulation(hoverfile, *pullOverrideHostFlag)

			if err := localCache.WriteSimulation(hoverfile, data); err == nil {
				fmt.Println(hoverfile.String(), "has been pulled from the Specto Lab")
			} else {
				failAndExit(err)
			}

		case wipeCommand.FullCommand():
			if err := hoverfly.Wipe(); err == nil {
				fmt.Println("Hoverfly has been wiped")
			} else {
				failAndExit(err)
			}
	}
}

func failAndExit(err error) {
	fmt.Println(err.Error())
	os.Exit(1)
}

func createHoverfly(hostOverride, adminPortOverride, proxyPortOverride string) Hoverfly {
	hoverfly := Hoverfly {
		Host: viper.GetString("hoverfly.host"),
		AdminPort: viper.GetString("hoverfly.admin.port"),
		ProxyPort: viper.GetString("hoverfly.proxy.port"),
		httpClient: http.DefaultClient,
	}

	if len(*hostFlag) > 0 {
		hoverfly.Host = *hostFlag
	}

	if len(*adminPortFlag) > 0 {
		hoverfly.AdminPort = *adminPortFlag
	}

	if len(*proxyPortFlag) > 0 {
		hoverfly.ProxyPort = *proxyPortFlag
	}

	return hoverfly
}

func setConfigurationDefaults() {
	viper.AddConfigPath("./.hoverfly")
	viper.AddConfigPath("$HOME/.hoverfly")
	viper.SetDefault("hoverfly.host", "localhost")
	viper.SetDefault("hoverfly.admin.port", "8888")
	viper.SetDefault("hoverfly.proxy.port", "8500")
	viper.SetDefault("specto.lab.host", "localhost")
	viper.SetDefault("specto.lab.port", "81")
}

func getHoverflyDirectory(configUri string) string {
	if len(configUri) == 0 {
		fmt.Println("Missing a config file")
		fmt.Println("Creating a new  a config file")

		hoverflyDir, err := createHomeDirectory()

		if err != nil {
			failAndExit(err)
		}

		return hoverflyDir
	}

	return path.Dir(configUri)
}<commit_msg>Will create a config file if you don't have one<commit_after>package main

import (
	"fmt"
	"os"
	"net/http"
	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/spf13/viper"
	"path"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"path/filepath"
)

var (
	hostFlag = kingpin.Flag("host", "Set the host of Hoverfly").String()
	adminPortFlag = kingpin.Flag("admin-port", "Set the admin port of Hoverfly").String()
	proxyPortFlag = kingpin.Flag("proxy-port", "Set the admin port of Hoverfly").String()

	modeCommand = kingpin.Command("mode", "Get Hoverfly's current mode")
	modeNameArg = modeCommand.Arg("name", "Set Hoverfly's mode").String()

	startCommand = kingpin.Command("start", "Start a local instance of Hoverfly")
	stopCommand = kingpin.Command("stop", "Stop a local instance of Hoverfly")

	exportCommand = kingpin.Command("export", "Exports data out of Hoverfly")
	exportNameArg = exportCommand.Arg("name", "Name of exported simulation").Required().String()

	importCommand = kingpin.Command("import", "Imports data into Hoverfly")
	importNameArg = importCommand.Arg("name", "Name of imported simulation").Required().String()

	pushCommand = kingpin.Command("push", "Pushes the data to Specto Hub")
	pushNameArg = pushCommand.Arg("name", "Name of exported simulation").Required().String()

	pullCommand = kingpin.Command("pull", "Pushes the data to Specto Hub")
	pullNameArg = pullCommand.Arg("name", "Name of imported simulation").Required().String()
	pullOverrideHostFlag = pullCommand.Flag("override-host", "Name of the host you want to virtualise").String()

	wipeCommand = kingpin.Command("wipe", "Wipe Hoverfly database")
)

func main() {
	setConfigurationDefaults()

	viper.ReadInConfig()

	configUri := viper.ConfigFileUsed()

	hoverflyDirectory := getHoverflyDirectory(configUri)

	cacheDirectory, err := createCacheDirectory(hoverflyDirectory)
	if err != nil {
		failAndExit(err)
	}

	localCache := LocalCache{
		Uri: cacheDirectory,
	}

	kingpin.Parse()

	hoverfly := createHoverfly(*hostFlag, *adminPortFlag, *proxyPortFlag)

	spectoLab := SpectoLab{
		Host: viper.GetString("specto.lab.host"),
		Port: viper.GetString("specto.lab.port"),
		ApiKey: viper.GetString("specto.lab.api.key"),
	}

	switch kingpin.Parse() {
		case modeCommand.FullCommand():
			if *modeNameArg == "" || *modeNameArg == "status"{

				mode, err := hoverfly.GetMode()
				if err == nil {
					fmt.Println("Hoverfly is set to", mode, "mode")
				} else {
					failAndExit(err)
				}

			} else {

				mode, err := hoverfly.SetMode(*modeNameArg)
				if err == nil {
					fmt.Println("Hoverfly has been set to", mode, "mode")
				} else {
					failAndExit(err)
				}

			}

		case startCommand.FullCommand():
			err := startHandler(hoverflyDirectory, hoverfly)
			if err != nil {
				failAndExit(err)
			}

		case stopCommand.FullCommand():
			stopHandler(hoverflyDirectory, hoverfly)

		case exportCommand.FullCommand():
			hoverfile, err := NewHoverfile(*exportNameArg)
			if err != nil {
				failAndExit(err)
			}

			exportedData, err := hoverfly.ExportSimulation()

			if err != nil {
				failAndExit(err)
			}

			if err = localCache.WriteSimulation(hoverfile, exportedData); err == nil {
				fmt.Println(*exportNameArg, "exported successfully")
			} else {
				failAndExit(err)
			}

		case importCommand.FullCommand():
			hoverfile, err := NewHoverfile(*importNameArg)
			if err != nil {
				failAndExit(err)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExit(err)
			}

			if err = hoverfly.ImportSimulation(string(data)); err == nil {
				fmt.Println(hoverfile.String(), "imported successfully")
			} else {
				failAndExit(err)
			}

		case pushCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pushNameArg)
			if err != nil {
				failAndExit(err)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExit(err)
			}

			statusCode, err := spectoLab.UploadSimulation(hoverfile, data)
			if err != nil {
				failAndExit(err)
			}

			if statusCode == 200 {
				fmt.Println(hoverfile.String(), "has been pushed to the Specto Lab")
			}

		case pullCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pullNameArg)
			if err != nil {
				failAndExit(err)
			}

			data := spectoLab.GetSimulation(hoverfile, *pullOverrideHostFlag)

			if err := localCache.WriteSimulation(hoverfile, data); err == nil {
				fmt.Println(hoverfile.String(), "has been pulled from the Specto Lab")
			} else {
				failAndExit(err)
			}

		case wipeCommand.FullCommand():
			if err := hoverfly.Wipe(); err == nil {
				fmt.Println("Hoverfly has been wiped")
			} else {
				failAndExit(err)
			}
	}
}

func failAndExit(err error) {
	fmt.Println(err.Error())
	os.Exit(1)
}

func createHoverfly(hostOverride, adminPortOverride, proxyPortOverride string) Hoverfly {
	hoverfly := Hoverfly {
		Host: viper.GetString("hoverfly.host"),
		AdminPort: viper.GetString("hoverfly.admin.port"),
		ProxyPort: viper.GetString("hoverfly.proxy.port"),
		httpClient: http.DefaultClient,
	}

	if len(*hostFlag) > 0 {
		hoverfly.Host = *hostFlag
	}

	if len(*adminPortFlag) > 0 {
		hoverfly.AdminPort = *adminPortFlag
	}

	if len(*proxyPortFlag) > 0 {
		hoverfly.ProxyPort = *proxyPortFlag
	}

	return hoverfly
}

func setConfigurationDefaults() {
	viper.AddConfigPath("./.hoverfly")
	viper.AddConfigPath("$HOME/.hoverfly")
	viper.SetDefault("hoverfly.host", "localhost")
	viper.SetDefault("hoverfly.admin.port", "8888")
	viper.SetDefault("hoverfly.proxy.port", "8500")
	viper.SetDefault("specto.lab.host", "localhost")
	viper.SetDefault("specto.lab.port", "81")
}

func getHoverflyDirectory(configUri string) string {
	if len(configUri) == 0 {
		fmt.Println("Missing a config file")
		fmt.Println("Creating a new  a config file")

		hoverflyDir, err := createHomeDirectory()

		createConfigFile(hoverflyDir)

		if err != nil {
			failAndExit(err)
		}

		return hoverflyDir
	}

	return path.Dir(configUri)
}

type Config struct {
	HoverflyHost      string `yaml:"hoverfly.host"`
	HoverflyAdminPort string `yaml:"hoverfly.admin.port"`
	HoverflyProxyPort string `yaml:"hoverfly.proxy.port"`
	SpectoLabHost     string `yaml:"specto.lab.host"`
	SpectoLabPort     string `yaml:"specto.lab.port"`
}

func createConfigFile(hoverflyDir string) {
	config := Config{
		HoverflyHost: viper.GetString("hoverfly.host"),
		HoverflyAdminPort: viper.GetString("hoverfly.admin.port"),
		HoverflyProxyPort: viper.GetString("hoverfly.proxy.port"),
		SpectoLabHost: viper.GetString("specto.lab.host"),
		SpectoLabPort: viper.GetString("specto.lab.port"),
	}
	configData, err := yaml.Marshal(config)

	if err != nil {
		failAndExit(err)
	}

	filename := filepath.Join(hoverflyDir, "config.yaml")

	err = ioutil.WriteFile(filename, configData, 0644)

	if err != nil {
		failAndExit(err)
	}
}<|endoftext|>


<commit_before>package main

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/loadimpact/speedboat/loadtest"
	"github.com/loadimpact/speedboat/runner"
	"github.com/loadimpact/speedboat/runner/js"
	"github.com/loadimpact/speedboat/runner/simple"
	"golang.org/x/net/context"
	"io/ioutil"
	"os"
	"path"
	"time"
)

func getRunner(filename, url string) (runner.Runner, error) {
	// TODO: Implement a URL runner.
	if url != "" {
		return nil, nil
	}

	switch path.Ext(filename) {
	case ".js":
		return js.New()
	default:
		return nil, errors.New("No runner found")
	}
}

func makeTest(c *cli.Context) (test loadtest.LoadTest, err error) {
	base := ""
	conf := loadtest.NewConfig()
	if len(c.Args()) > 0 {
		filename := c.Args()[0]
		base = path.Dir(filename)
		data, err := ioutil.ReadFile(filename)
		if err != nil {
			return test, err
		}

		loadtest.ParseConfig(data, &conf)
	}

	if c.IsSet("script") {
		conf.Script = c.String("script")
		base = ""
	}
	if c.IsSet("url") {
		conf.URL = c.String("url")
	}
	if c.IsSet("duration") {
		conf.Duration = c.Duration("duration").String()
	}
	if c.IsSet("vus") {
		conf.VUs = c.Int("vus")
	}

	test, err = conf.Compile()
	if err != nil {
		return test, err
	}

	if test.Script != "" {
		srcb, err := ioutil.ReadFile(path.Join(base, test.Script))
		if err != nil {
			return test, err
		}
		test.Source = string(srcb)
	}

	return test, nil
}

func action(c *cli.Context) {
	test, err := makeTest(c)
	if err != nil {
		log.WithError(err).Fatal("Configuration error")
	}

	r := simple.New()
	r.URL = test.URL

	ctx, cancel := context.WithCancel(context.Background())
	i := 0
	for t := range r.Run(ctx) {
		log.WithField("t", t).Info("Test Metric")

		i++
		if i >= 10 {
			cancel()
		}
	}

	// 	r, err := getRunner(test.Script, test.URL)
	// 	if err != nil {
	// 		log.WithError(err).Fatal("Couldn't get a runner")
	// 	}

	// 	err = r.Load(test.Script, test.Source)
	// 	if err != nil {
	// 		log.WithError(err).Fatal("Couldn't load script")
	// 	}

	// 	// Write a number to the control channel to make the test scale to that many
	// 	// VUs; close it to make the test terminate.
	// 	controlChannel := make(chan int, 1)
	// 	controlChannel <- test.Stages[0].VUs.Start

	// 	sequencer := runner.NewSequencer()
	// 	startTime := time.Now()

	// 	intervene := time.NewTicker(time.Duration(1) * time.Second)
	// 	results := runner.Run(r, controlChannel)
	// runLoop:
	// 	for {
	// 		select {
	// 		case res, ok := <-results:
	// 			// The results channel will be closed once all VUs are done.
	// 			if !ok {
	// 				break runLoop
	// 			}
	// 			switch res := res.(type) {
	// 			case runner.LogEntry:
	// 				log.WithField("text", res.Text).Info("Test Log")
	// 			case runner.Metric:
	// 				log.WithField("d", res.Duration).Debug("Test Metric")
	// 				sequencer.Add(res)
	// 			case error:
	// 				log.WithError(res).Error("Test Error")
	// 			}
	// 		case <-intervene.C:
	// 			vus, stop := test.VUsAt(time.Since(startTime))
	// 			if stop {
	// 				// Stop the timer, and let VUs gracefully terminate.
	// 				intervene.Stop()
	// 				close(controlChannel)
	// 			} else {
	// 				controlChannel <- vus
	// 			}
	// 		}
	// 	}

	// 	stats := sequencer.Stats()
	// 	log.WithField("count", sequencer.Count()).Info("Results")
	// 	log.WithFields(log.Fields{
	// 		"min": stats.Duration.Min,
	// 		"max": stats.Duration.Max,
	// 		"avg": stats.Duration.Avg,
	// 		"med": stats.Duration.Med,
	// 	}).Info("Duration")
}

// Configure the global logger.
func configureLogging(c *cli.Context) {
	if c.GlobalBool("verbose") {
		log.SetLevel(log.DebugLevel)
	}
}

func main() {
	// Free up -v and -h for our own flags
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help, ?"

	// Bootstrap using action-registered commandline flags
	app := cli.NewApp()
	app.Name = "speedboat"
	app.Usage = "A next-generation load generator"
	app.Version = "0.0.1a1"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "More verbose output",
		},
		cli.StringFlag{
			Name:  "script, s",
			Usage: "Script to run (do not use with --url)",
		},
		cli.StringFlag{
			Name:  "url",
			Usage: "URL to test (do not use with --script)",
		},
		cli.IntFlag{
			Name:  "vus, u",
			Usage: "Number of VUs to simulate",
			Value: 10,
		},
		cli.DurationFlag{
			Name:  "duration, d",
			Usage: "Test duration",
			Value: time.Duration(10) * time.Second,
		},
	}
	app.Before = func(c *cli.Context) error {
		configureLogging(c)
		return nil
	}
	app.Action = action
	app.Run(os.Args)
}
<commit_msg>Use the builtin timeout feature<commit_after>package main

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/loadimpact/speedboat/loadtest"
	"github.com/loadimpact/speedboat/runner"
	"github.com/loadimpact/speedboat/runner/js"
	"github.com/loadimpact/speedboat/runner/simple"
	"golang.org/x/net/context"
	"io/ioutil"
	"os"
	"path"
	"time"
)

func getRunner(filename, url string) (runner.Runner, error) {
	// TODO: Implement a URL runner.
	if url != "" {
		return nil, nil
	}

	switch path.Ext(filename) {
	case ".js":
		return js.New()
	default:
		return nil, errors.New("No runner found")
	}
}

func makeTest(c *cli.Context) (test loadtest.LoadTest, err error) {
	base := ""
	conf := loadtest.NewConfig()
	if len(c.Args()) > 0 {
		filename := c.Args()[0]
		base = path.Dir(filename)
		data, err := ioutil.ReadFile(filename)
		if err != nil {
			return test, err
		}

		loadtest.ParseConfig(data, &conf)
	}

	if c.IsSet("script") {
		conf.Script = c.String("script")
		base = ""
	}
	if c.IsSet("url") {
		conf.URL = c.String("url")
	}
	if c.IsSet("duration") {
		conf.Duration = c.Duration("duration").String()
	}
	if c.IsSet("vus") {
		conf.VUs = c.Int("vus")
	}

	test, err = conf.Compile()
	if err != nil {
		return test, err
	}

	if test.Script != "" {
		srcb, err := ioutil.ReadFile(path.Join(base, test.Script))
		if err != nil {
			return test, err
		}
		test.Source = string(srcb)
	}

	return test, nil
}

func action(c *cli.Context) {
	test, err := makeTest(c)
	if err != nil {
		log.WithError(err).Fatal("Configuration error")
	}

	r := simple.New()
	r.URL = test.URL

	timeout := time.Duration(0)
	for _, stage := range test.Stages {
		timeout += stage.Duration
	}

	ctx, _ := context.WithTimeout(context.Background(), timeout)
	for t := range r.Run(ctx) {
		log.WithField("t", t).Info("Test Metric")
	}

	// 	r, err := getRunner(test.Script, test.URL)
	// 	if err != nil {
	// 		log.WithError(err).Fatal("Couldn't get a runner")
	// 	}

	// 	err = r.Load(test.Script, test.Source)
	// 	if err != nil {
	// 		log.WithError(err).Fatal("Couldn't load script")
	// 	}

	// 	// Write a number to the control channel to make the test scale to that many
	// 	// VUs; close it to make the test terminate.
	// 	controlChannel := make(chan int, 1)
	// 	controlChannel <- test.Stages[0].VUs.Start

	// 	sequencer := runner.NewSequencer()
	// 	startTime := time.Now()

	// 	intervene := time.NewTicker(time.Duration(1) * time.Second)
	// 	results := runner.Run(r, controlChannel)
	// runLoop:
	// 	for {
	// 		select {
	// 		case res, ok := <-results:
	// 			// The results channel will be closed once all VUs are done.
	// 			if !ok {
	// 				break runLoop
	// 			}
	// 			switch res := res.(type) {
	// 			case runner.LogEntry:
	// 				log.WithField("text", res.Text).Info("Test Log")
	// 			case runner.Metric:
	// 				log.WithField("d", res.Duration).Debug("Test Metric")
	// 				sequencer.Add(res)
	// 			case error:
	// 				log.WithError(res).Error("Test Error")
	// 			}
	// 		case <-intervene.C:
	// 			vus, stop := test.VUsAt(time.Since(startTime))
	// 			if stop {
	// 				// Stop the timer, and let VUs gracefully terminate.
	// 				intervene.Stop()
	// 				close(controlChannel)
	// 			} else {
	// 				controlChannel <- vus
	// 			}
	// 		}
	// 	}

	// 	stats := sequencer.Stats()
	// 	log.WithField("count", sequencer.Count()).Info("Results")
	// 	log.WithFields(log.Fields{
	// 		"min": stats.Duration.Min,
	// 		"max": stats.Duration.Max,
	// 		"avg": stats.Duration.Avg,
	// 		"med": stats.Duration.Med,
	// 	}).Info("Duration")
}

// Configure the global logger.
func configureLogging(c *cli.Context) {
	if c.GlobalBool("verbose") {
		log.SetLevel(log.DebugLevel)
	}
}

func main() {
	// Free up -v and -h for our own flags
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help, ?"

	// Bootstrap using action-registered commandline flags
	app := cli.NewApp()
	app.Name = "speedboat"
	app.Usage = "A next-generation load generator"
	app.Version = "0.0.1a1"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "More verbose output",
		},
		cli.StringFlag{
			Name:  "script, s",
			Usage: "Script to run (do not use with --url)",
		},
		cli.StringFlag{
			Name:  "url",
			Usage: "URL to test (do not use with --script)",
		},
		cli.IntFlag{
			Name:  "vus, u",
			Usage: "Number of VUs to simulate",
			Value: 10,
		},
		cli.DurationFlag{
			Name:  "duration, d",
			Usage: "Test duration",
			Value: time.Duration(10) * time.Second,
		},
	}
	app.Before = func(c *cli.Context) error {
		configureLogging(c)
		return nil
	}
	app.Action = action
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/PuerkitoBio/goquery"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/urfave/cli"
)

var (
	Version  string
	Revision string
)

type configFile struct {
	Urls      []string `toml:"urls"`
	UserAgent string   `toml:"useragent"`
}

func main() {
	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
	}
	app := cli.NewApp()
	app.Name = "gocrawsan"
	app.Usage = "web crawling command utility"
	app.Version = Version
	app.Usage = ""
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "useragent, U",
		},
		cli.StringFlag{
			Name: "config, C",
		},
		cli.BoolFlag{
			Name: "no-redirect",
		},
		cli.StringFlag{
			Name: "selector, S",
		},
		cli.StringFlag{
			Name: "pick-type, P",
		},
		cli.StringFlag{
			Name: "attribute, A",
		},
		cli.BoolFlag{
			Name: "no-error",
		},
		cli.IntFlag{
			Name: "timeout",
			Value: 10,
		},
		cli.IntFlag{
			Name:  "depth, D",
			Value: 1,
		},
	}
	app.Action = func(c *cli.Context) error {
		err := validate(c)
		if err != nil {
			return err
		}
		cr := NewCrawler()
		cr.useragent = c.String("useragent")
		client := &http.Client{}
		client.Timeout = time.Duration(time.Duration(c.Int("timeout")) * time.Second)
		if c.Bool("no-redirect") {
			client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			}
		}
		cr.client = client

		file, err := readOrCreateConfigFile(c)
		if err != nil {
			return err
		}
		if file == nil {
			return nil
		}
		var f func(string, *http.Response)
		if c.String("selector") != "" {
			f = cr.printWithSelector(c.String("selector"), c.String("pick-type"), c.String("attribute"))
		} else {
			f = cr.printHttpStatus
		}
		cr.crawl(file.Urls, f, c.Int("depth"))
		if len(cr.errors) > 0 {
			return &multipleError{errors: cr.errors}
		}
		return nil
	}
	app.Run(os.Args)
}

type Crawler struct {
	m            *sync.Mutex
	wg           *sync.WaitGroup
	useragent    string
	client       *http.Client
	accessedUrls map[string]struct{}
	errors       []error
}

func NewCrawler() *Crawler {
	c := &Crawler{
		wg:           new(sync.WaitGroup),
		m:            new(sync.Mutex),
		accessedUrls: make(map[string]struct{}),
		errors:       []error{},
	}
	return c
}

func (c *Crawler) crawl(urls []string, f func(string, *http.Response), depth int) {
	for _, url := range urls {
		c.m.Lock()
		if _, ok := c.accessedUrls[url]; ok {
			c.m.Unlock()
			continue
		}
		c.accessedUrls[url] = struct{}{}
		c.m.Unlock()

		c.wg.Add(1)
		go c.getUrl(url, f, depth)
	}
	c.wg.Wait()
}

func (c *Crawler) getUrl(url string, f func(string, *http.Response), d int) {
	defer c.wg.Done()
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", c.useragent)
	resp, err := c.client.Do(req)
	if err != nil {
		c.errors = append(c.errors, err)
		return
	}
	d -= 1
	f(url, resp)
	c.accessToNext(resp, f, d)
}

func (c *Crawler) printHttpStatus(url string, resp *http.Response) {
	c.m.Lock()
	status := strings.Split(resp.Status, " ")
	code, _ := strconv.Atoi(status[0])
	fmt.Print(url + "\t")
	switch code / 100 {
	case 2:
		color.Cyan(resp.Status)
	case 3:
		color.Yellow(resp.Status)
	case 4:
		color.Red(resp.Status)
	default:
		fmt.Println(resp.Status)
	}
	c.m.Unlock()
}

func (c *Crawler) printWithSelector(selector string, pickType string, pickValue string) func(string, *http.Response) {
	return func(url string, resp *http.Response) {
		c.m.Lock()
		printWithSelector(selector, pickType, pickValue, url, resp)
		c.m.Unlock()
	}
}

func printWithSelector(selector string, pickType string, pickValue string, url string, resp *http.Response) {
	doc, _ := goquery.NewDocumentFromResponse(resp)
	doc.Find(selector).Each(func(_ int, s *goquery.Selection) {
		var text string
		if pickType == "text" {
			text = s.Text()
		} else if pickType == "attr" {
			text, _ = s.Attr(pickValue)
		}
		fmt.Println(text)
	})
}

func (c *Crawler) accessToNext(resp *http.Response, f func(string, *http.Response), d int) error {
	if d <= 0 {
		return nil
	}
	links, err := getLinks(resp)
	if err != nil {
		return err
	}
	for _, link := range links {
		c.m.Lock()
		if _, ok := c.accessedUrls[link]; ok {
			c.m.Unlock()
			continue
		}
		c.accessedUrls[link] = struct{}{}
		c.m.Unlock()
		c.wg.Add(1)
		go c.getUrl(link, f, d)
	}
	return nil
}

func getLinks(res *http.Response) ([]string, error) {
	urls := []string{}
	doc, _ := goquery.NewDocumentFromResponse(res)
	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
		url, _ := s.Attr("href")
		r := regexp.MustCompile(`^(https|http)://(.*)`)
		if !r.MatchString(url) {
			url = res.Request.URL.Scheme + "://" + res.Request.URL.Host + url
		}
		urls = append(urls, url)
	})
	return urls, nil
}

func createConfigFile() (string, error) {
	dir, err := configDir()
	if err != nil {
		return "", err
	}
	if err = os.MkdirAll(dir, 0700); err != nil {
		return "", err
	}
	config := filepath.Join(dir, "config.toml")
	if _, err := os.Stat(config); err != nil {
		reader := bufio.NewReader(os.Stdin)

		fmt.Println("Do you create configfile in " + config + "?(y/N): ")
		answer, _ := reader.ReadString('\n')
		if answer == "y" || answer == "Y" {
			err = ioutil.WriteFile(config, []byte("urls = [\"https://example.com\"]"), 0644)
			if err != nil {
				return "", err
			}
			fmt.Println("successful to create config file.")
		}
	}
	return config, nil
}

func configDir() (string, error) {
	home, err := homedir.Dir()
	if err != nil {
		return "", err
	}
	dir := filepath.Join(home, ".config", "gocrawsan")
	return dir, nil
}

type multipleError struct {
	errors []error
}

func (e *multipleError) Error() string {
	errorStrings := []string{}
	for _, err := range e.errors {
		errorStrings = append(errorStrings, err.Error())
	}
	return strings.Join(errorStrings, "\n")
}

func readOrCreateConfigFile(c *cli.Context) (*configFile, error) {
	var config string
	var err error
	if c.String("config") == "" {
		config, err = createConfigFile()
		if err != nil {
			return nil, err
		}
		if config == "" {
			return nil, nil
		}
	} else {
		config = c.String("config")
	}

	buf, err := ioutil.ReadFile(config)
	if err != nil {
		return nil, err
	}
	file := &configFile{}
	if err = toml.Unmarshal(buf, file); err != nil {
		return nil, err
	}
	return file, nil
}

func validate(c *cli.Context) error {
	pickType := map[string]bool{
		"text": true,
		"attr": true,
	}
	if c.String("pick-type") != "" && !pickType[c.String("pick-type")] {
		return errors.New("Invalid pick-type. please set 'text' or 'attr'")
	}
	if c.String("selector") != "" && c.String("pick-type") == "" {
		return errors.New("if you set selector option, please set pick-type option too")
	}
	if c.String("pick-type") == "attr" && c.String("attribute") == "" {
		return errors.New("if your set 'attr' to pick-type option, please set attribute")
	}
	return nil
}

<commit_msg>Fix bug<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/PuerkitoBio/goquery"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/urfave/cli"
)

var (
	Version  string
	Revision string
)

type configFile struct {
	Urls      []string `toml:"urls"`
	UserAgent string   `toml:"useragent"`
}

func main() {
	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
	}
	app := cli.NewApp()
	app.Name = "gocrawsan"
	app.Usage = "web crawling command utility"
	app.Version = Version
	app.Usage = ""
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "useragent, U",
		},
		cli.StringFlag{
			Name: "config, C",
		},
		cli.BoolFlag{
			Name: "no-redirect",
		},
		cli.StringFlag{
			Name: "selector, S",
		},
		cli.StringFlag{
			Name: "pick-type, P",
		},
		cli.StringFlag{
			Name: "attribute, A",
		},
		cli.BoolFlag{
			Name: "no-error",
		},
		cli.IntFlag{
			Name: "timeout",
			Value: 10,
		},
		cli.IntFlag{
			Name:  "depth, D",
			Value: 1,
		},
	}
	app.Action = func(c *cli.Context) error {
		err := validate(c)
		if err != nil {
			return err
		}
		cr := NewCrawler()
		cr.useragent = c.String("useragent")
		client := &http.Client{}
		client.Timeout = time.Duration(time.Duration(c.Int("timeout")) * time.Second)
		if c.Bool("no-redirect") {
			client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			}
		}
		cr.client = client

		file, err := readOrCreateConfigFile(c)
		if err != nil {
			return err
		}
		if file == nil {
			return nil
		}
		var f func(string, *http.Response)
		if c.String("selector") != "" {
			f = cr.printWithSelector(c.String("selector"), c.String("pick-type"), c.String("attribute"))
		} else {
			f = cr.printHttpStatus
		}
		cr.crawl(file.Urls, f, c.Int("depth"))
		if len(cr.errors) > 0 {
			return &multipleError{errors: cr.errors}
		}
		return nil
	}
	app.Run(os.Args)
}

type Crawler struct {
	m            *sync.Mutex
	wg           *sync.WaitGroup
	useragent    string
	client       *http.Client
	accessedUrls map[string]struct{}
	errors       []error
}

func NewCrawler() *Crawler {
	c := &Crawler{
		wg:           new(sync.WaitGroup),
		m:            new(sync.Mutex),
		accessedUrls: make(map[string]struct{}),
		errors:       []error{},
	}
	return c
}

func (c *Crawler) crawl(urls []string, f func(string, *http.Response), depth int) {
	for _, url := range urls {
		c.m.Lock()
		if _, ok := c.accessedUrls[url]; ok {
			c.m.Unlock()
			continue
		}
		c.accessedUrls[url] = struct{}{}
		c.m.Unlock()

		c.wg.Add(1)
		go c.getUrl(url, f, depth)
	}
	c.wg.Wait()
}

func (c *Crawler) getUrl(url string, f func(string, *http.Response), d int) {
	defer c.wg.Done()
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", c.useragent)
	resp, err := c.client.Do(req)
	if err != nil {
		c.errors = append(c.errors, err)
		return
	}
	d -= 1
	f(url, resp)
	c.accessToNext(resp, f, d)
}

func (c *Crawler) printHttpStatus(url string, resp *http.Response) {
	c.m.Lock()
	status := strings.Split(resp.Status, " ")
	code, _ := strconv.Atoi(status[0])
	fmt.Print(url + "\t")
	switch code / 100 {
	case 2:
		color.Cyan(resp.Status)
	case 3:
		color.Yellow(resp.Status)
	case 4:
		color.Red(resp.Status)
	default:
		fmt.Println(resp.Status)
	}
	c.m.Unlock()
}

func (c *Crawler) printWithSelector(selector string, pickType string, pickValue string) func(string, *http.Response) {
	return func(url string, resp *http.Response) {
		c.m.Lock()
		printWithSelector(selector, pickType, pickValue, url, resp)
		c.m.Unlock()
	}
}

func printWithSelector(selector string, pickType string, pickValue string, url string, resp *http.Response) {
	doc, _ := goquery.NewDocumentFromResponse(resp)
	doc.Find(selector).Each(func(_ int, s *goquery.Selection) {
		var text string
		if pickType == "text" {
			text = s.Text()
		} else if pickType == "attr" {
			text, _ = s.Attr(pickValue)
		}
		fmt.Println(text)
	})
}

func (c *Crawler) accessToNext(resp *http.Response, f func(string, *http.Response), d int) error {
	if d <= 0 {
		return nil
	}
	links, err := getLinks(resp)
	if err != nil {
		return err
	}
	for _, link := range links {
		c.m.Lock()
		if _, ok := c.accessedUrls[link]; ok {
			c.m.Unlock()
			continue
		}
		c.accessedUrls[link] = struct{}{}
		c.m.Unlock()
		c.wg.Add(1)
		go c.getUrl(link, f, d)
	}
	return nil
}

func getLinks(res *http.Response) ([]string, error) {
	urls := []string{}
	doc, _ := goquery.NewDocumentFromResponse(res)
	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
		url, _ := s.Attr("href")
		r := regexp.MustCompile(`^(https|http)://(.*)`)
		if !r.MatchString(url) {
			url = res.Request.URL.Scheme + "://" + res.Request.URL.Host + url
		}
		urls = append(urls, url)
	})
	return urls, nil
}

func createConfigFile() (string, error) {
	dir, err := configDir()
	if err != nil {
		return "", err
	}
	if err = os.MkdirAll(dir, 0700); err != nil {
		return "", err
	}
	config := filepath.Join(dir, "config.toml")
	if _, err := os.Stat(config); err != nil {
		reader := bufio.NewReader(os.Stdin)

		fmt.Print("Do you create configfile in " + config + "?(y/N): ")
		answer, _ := reader.ReadString('\n')
		answer = strings.Replace(answer, "\n", "", -1)
		if answer == "y" || answer == "Y" {
			err = ioutil.WriteFile(config, []byte("urls = [\"https://example.com\"]"), 0644)
			if err != nil {
				return "", err
			}
			fmt.Println("successful to create config file.")
		}
		return "", nil
	}
	return config, nil
}

func configDir() (string, error) {
	home, err := homedir.Dir()
	if err != nil {
		return "", err
	}
	dir := filepath.Join(home, ".config", "gocrawsan")
	return dir, nil
}

type multipleError struct {
	errors []error
}

func (e *multipleError) Error() string {
	errorStrings := []string{}
	for _, err := range e.errors {
		errorStrings = append(errorStrings, err.Error())
	}
	return strings.Join(errorStrings, "\n")
}

func readOrCreateConfigFile(c *cli.Context) (*configFile, error) {
	var config string
	var err error
	if c.String("config") == "" {
		config, err = createConfigFile()
		if err != nil {
			return nil, err
		}
		if config == "" {
			return nil, nil
		}
	} else {
		config = c.String("config")
	}

	buf, err := ioutil.ReadFile(config)
	if err != nil {
		return nil, err
	}
	file := &configFile{}
	if err = toml.Unmarshal(buf, file); err != nil {
		return nil, err
	}
	return file, nil
}

func validate(c *cli.Context) error {
	pickType := map[string]bool{
		"text": true,
		"attr": true,
	}
	if c.String("pick-type") != "" && !pickType[c.String("pick-type")] {
		return errors.New("Invalid pick-type. please set 'text' or 'attr'")
	}
	if c.String("selector") != "" && c.String("pick-type") == "" {
		return errors.New("if you set selector option, please set pick-type option too")
	}
	if c.String("pick-type") == "attr" && c.String("attribute") == "" {
		return errors.New("if your set 'attr' to pick-type option, please set attribute")
	}
	return nil
}

<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
)

var (
	dir   = flag.String("d", "budget", "budget data directory")
	month = flag.String("m", "", "year/month in format YYYYMM")
)

// Transaction is a single transaction with a cost and name
type Transaction struct {
	Cost float64
	Name string
}

// Budget is a monthly budget
type Budget struct {
	Total        float64
	Remaining    float64
	Transactions []Transaction
}

func parseFile(name string) (Budget, error) {
	file, err := os.Open(name)
	if err != nil {
		return Budget{}, err
	}
	r := bufio.NewReader(file)
	b := Budget{}
	// first line is the total for the month
	line, _, err := r.ReadLine()
	if err != nil {
		return Budget{}, err
	}
	total, err := strconv.ParseFloat(string(line), 64)
	if err != nil {
		return Budget{}, err
	}
	b.Total = total
	for {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}

		if err != nil {
			return Budget{}, err
		}

		t := strings.Split(string(line), ",")
		if len(t) != 2 {
			return Budget{}, fmt.Errorf("invalid line %q", line)
		}

		cost, err := strconv.ParseFloat(t[0], 64)
		if err != nil {
			return Budget{}, err
		}

		trans := Transaction{Cost: cost, Name: strings.TrimSpace(t[1])}

		b.Transactions = append(b.Transactions, trans)
	}

	b.Remaining = b.Total
	for _, trans := range b.Transactions {
		b.Remaining -= trans.Cost
	}

	return b, nil
}

// Pair is a data structure to hold a key/value pair.
// modified from https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
type Pair struct {
	Key   string
	Value float64
}

// PairList is a slice of Pairs that implements sort.Interface to sort by Value.
type PairList []Pair

func (p PairList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p PairList) Len() int           { return len(p) }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }

// A function to turn a map into a PairList, then sort and return it.
func sortMapByValue(m map[string]float64) PairList {
	p := make(PairList, len(m))
	i := 0

	for k, v := range m {
		p[i] = Pair{k, v}
		i++
	}

	sort.Reverse(p)
	return p
}

// borrowed from golang src time pkg
// daysBefore[m] counts the number of days in a non-leap year
// before month m begins.  There is an entry for m=12, counting
// the number of days before January of next year (365).
var daysBefore = [...]int32{
	0,
	31,
	31 + 28,
	31 + 28 + 31,
	31 + 28 + 31 + 30,
	31 + 28 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}

func isLeap(year int) bool {
	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}

func daysIn(m time.Month, year int) int {
	if m == time.February && isLeap(year) {
		return 29
	}
	return int(daysBefore[m] - daysBefore[m-1])
}

func main() {
	flag.Parse()
	b, err := parseFile(fmt.Sprintf("%s/%s.txt", *dir, *month))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Total:", b.Total)
	fmt.Println("Remaining:", b.Remaining)

	mon := *month
	year, err := strconv.Atoi(mon[0:4])
	if err != nil {
		log.Fatal(err)
	}

	m, err := strconv.Atoi(mon[4:6])
	if err != nil {
		log.Fatal(err)
	}

	rpd := b.Remaining / float64(daysIn(time.Month(m), year)-time.Now().Day())
	fmt.Printf("Remaining/day: %.2f\n", rpd)

	top := map[string]float64{}
	for _, t := range b.Transactions {
		top[t.Name] += t.Cost
	}

	fmt.Println("Top costs:")
	pl := sortMapByValue(top)
	for _, p := range pl {
		fmt.Printf("\t%s: %.2f\n", p.Key, p.Value)
	}
}
<commit_msg>fix sort reverse<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
)

var (
	dir   = flag.String("d", "budget", "budget data directory")
	month = flag.String("m", "", "year/month in format YYYYMM")
)

// Transaction is a single transaction with a cost and name
type Transaction struct {
	Cost float64
	Name string
}

// Budget is a monthly budget
type Budget struct {
	Total        float64
	Remaining    float64
	Transactions []Transaction
}

func parseFile(name string) (Budget, error) {
	file, err := os.Open(name)
	if err != nil {
		return Budget{}, err
	}
	r := bufio.NewReader(file)
	b := Budget{}
	// first line is the total for the month
	line, _, err := r.ReadLine()
	if err != nil {
		return Budget{}, err
	}
	total, err := strconv.ParseFloat(string(line), 64)
	if err != nil {
		return Budget{}, err
	}
	b.Total = total
	for {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}

		if err != nil {
			return Budget{}, err
		}

		t := strings.Split(string(line), ",")
		if len(t) != 2 {
			return Budget{}, fmt.Errorf("invalid line %q", line)
		}

		cost, err := strconv.ParseFloat(t[0], 64)
		if err != nil {
			return Budget{}, err
		}

		trans := Transaction{Cost: cost, Name: strings.TrimSpace(t[1])}

		b.Transactions = append(b.Transactions, trans)
	}

	b.Remaining = b.Total
	for _, trans := range b.Transactions {
		b.Remaining -= trans.Cost
	}

	return b, nil
}

// Pair is a data structure to hold a key/value pair.
// modified from https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
type Pair struct {
	Key   string
	Value float64
}

// PairList is a slice of Pairs that implements sort.Interface to sort by Value.
type PairList []Pair

func (p PairList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p PairList) Len() int           { return len(p) }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }

// A function to turn a map into a PairList, then sort and return it.
func sortMapByValue(m map[string]float64) PairList {
	p := make(PairList, len(m))
	i := 0

	for k, v := range m {
		p[i] = Pair{k, v}
		i++
	}

	sort.Sort(sort.Reverse(p))
	return p
}

// borrowed from golang src time pkg
// daysBefore[m] counts the number of days in a non-leap year
// before month m begins.  There is an entry for m=12, counting
// the number of days before January of next year (365).
var daysBefore = [...]int32{
	0,
	31,
	31 + 28,
	31 + 28 + 31,
	31 + 28 + 31 + 30,
	31 + 28 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}

func isLeap(year int) bool {
	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}

func daysIn(m time.Month, year int) int {
	if m == time.February && isLeap(year) {
		return 29
	}
	return int(daysBefore[m] - daysBefore[m-1])
}

func main() {
	flag.Parse()
	b, err := parseFile(fmt.Sprintf("%s/%s.txt", *dir, *month))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Total:", b.Total)
	fmt.Println("Remaining:", b.Remaining)

	mon := *month
	year, err := strconv.Atoi(mon[0:4])
	if err != nil {
		log.Fatal(err)
	}

	m, err := strconv.Atoi(mon[4:6])
	if err != nil {
		log.Fatal(err)
	}

	rpd := b.Remaining / float64(daysIn(time.Month(m), year)-time.Now().Day())
	fmt.Printf("Remaining/day: %.2f\n", rpd)

	top := map[string]float64{}
	for _, t := range b.Transactions {
		top[t.Name] += t.Cost
	}

	fmt.Println("Top costs:")
	pl := sortMapByValue(top)
	for _, p := range pl {
		fmt.Printf("\t%s: %.2f\n", p.Key, p.Value)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/pkg/errors"
	"log"
	"os"
	"path"
)

var red (func(...interface{}) string) = color.New(color.FgRed).SprintFunc()
var yellow (func(...interface{}) string) = color.New(color.FgYellow).SprintFunc()
var green (func(...interface{}) string) = color.New(color.FgGreen).SprintFunc()
var blue (func(...interface{}) string) = color.New(color.FgCyan).SprintFunc()

func main() {
	pluginDir, err := findNvimPluginDir()
	if err != nil {
		log.Println(red(err))
	}
	fmt.Println(blue(pluginDir) + " targeted")
	vimDir, err := findVimPluginDir()
	if err != nil {
		log.Println(red(err))
	}
	fmt.Println(blue(vimDir) + " targeted")
}

// findNvimPluginDir returns the directory in which neovim stores its plugins
// or the empty string if it was unable to find the directory
func findNvimPluginDir() (string, error) {
	xdgHome := os.Getenv("XDG_CONFIG_HOME")
	if len(xdgHome) < 1 {
		fmt.Println(yellow("Environment Variable XDG_CONFIG_HOME" +
			" is not set or has no value. Please set this if" +
			" you use neovim. Inferring default value for" +
			" XDG_CONFIG_HOME..."))
		xdgHome = path.Join(os.Getenv("HOME"), ".config")
	}

	fmt.Println("XDG_CONFIG_HOME=" + xdgHome)

	configPath := path.Join(xdgHome, "nvim")
	configDir, err := os.Open(configPath)
	if err != nil {
		return "", errors.Wrapf(err, "Unable to find %s directory:", configPath)
	}
	defer configDir.Close()
	fmt.Println(green(configPath + " exists"))
	bundlePath := path.Join(configPath, "bundle")
	bundleDir, err := os.Open(bundlePath)
	if err != nil {
		return "", errors.Wrapf(err, "Unable to find %s directory:", bundlePath)
	}
	defer bundleDir.Close()
	fmt.Println(green(bundlePath + " exists"))
	return bundlePath, nil
}

// findNvimPluginDir returns the directory in which neovim stores its plugins
// or the empty string if it was unable to find the directory
func findVimPluginDir() (string, error) {
	vimConfig := os.Getenv("HOME")
	fmt.Println("HOME=" + vimConfig)

	configPath := path.Join(vimConfig, ".vim")
	configDir, err := os.Open(configPath)
	if err != nil {
		return "", errors.Wrapf(err, "Unable to find %s directory:", configPath)
	}
	defer configDir.Close()
	fmt.Println(green(configPath + " exists"))
	bundlePath := path.Join(configPath, "bundle")
	bundleDir, err := os.Open(bundlePath)
	if err != nil {
		return "", errors.Wrapf(err, "Unable to find %s directory:", bundlePath)
	}
	defer bundleDir.Close()
	fmt.Println(green(bundlePath + " exists"))
	return bundlePath, nil
}
<commit_msg>Refactor to remove redundant existence checks<commit_after>package main

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/pkg/errors"
	"log"
	"os"
	"path"
)

var red (func(...interface{}) string) = color.New(color.FgRed).SprintFunc()
var yellow (func(...interface{}) string) = color.New(color.FgYellow).SprintFunc()
var green (func(...interface{}) string) = color.New(color.FgGreen).SprintFunc()
var blue (func(...interface{}) string) = color.New(color.FgCyan).SprintFunc()

func main() {
	pluginDir, err := findNvimPluginDir()
	if err != nil {
		log.Println(red(err))
	}
	fmt.Println(blue(pluginDir) + " targeted")
	vimDir, err := findVimPluginDir()
	if err != nil {
		log.Println(red(err))
	}
	fmt.Println(blue(vimDir) + " targeted")
}

// findNvimPluginDir returns the directory in which neovim stores its plugins
// or the empty string if it was unable to find the directory
func findNvimPluginDir() (string, error) {
	xdgHome := os.Getenv("XDG_CONFIG_HOME")
	if len(xdgHome) < 1 {
		fmt.Println(yellow("Environment Variable XDG_CONFIG_HOME" +
			" is not set or has no value. Please set this if" +
			" you use neovim. Inferring default value for" +
			" XDG_CONFIG_HOME..."))
		xdgHome = path.Join(os.Getenv("HOME"), ".config")
	}

	fmt.Println("XDG_CONFIG_HOME=" + xdgHome)
	bundlePath := path.Join(xdgHome, "nvim", "bundle")
	bundleDir, err := os.Open(bundlePath)
	if err != nil {
		return "", errors.Wrapf(err, "Unable to find %s directory:", bundlePath)
	}
	defer bundleDir.Close()
	fmt.Println(green(bundlePath + " exists"))
	return bundlePath, nil
}

// findNvimPluginDir returns the directory in which neovim stores its plugins
// or the empty string if it was unable to find the directory
func findVimPluginDir() (string, error) {
	home := os.Getenv("HOME")
	fmt.Println("HOME=" + home)

	bundlePath := path.Join(home, ".vim", "bundle")
	bundleDir, err := os.Open(bundlePath)
	if err != nil {
		return "", errors.Wrapf(err, "Unable to find %s directory:", bundlePath)
	}
	defer bundleDir.Close()
	fmt.Println(green(bundlePath + " exists"))
	return bundlePath, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"bufio"
	"encoding/binary"
	"encoding/json"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"image/png"
	"image/jpeg"

	"github.com/layeh/gopus"
)

// Define constants
const (
	// The current version of the DCA format
	FormatVersion int8 = 1

	// The current version of the DCA program
	ProgramVersion string = "0.0.1"

	// The URL to the GitHub repository of DCA
	GitHubRepositoryURL string = "https://github.com/bwmarrin/dca"
)

// All global variables used within the program
var (
	// Buffer for some commands
	CmdBuf bytes.Buffer
	PngBuf bytes.Buffer

	// Metadata structures
	Metadata	MetadataStruct
	FFprobeData FFprobeMetadata

	// Magic bytes to write at the start of a DCA file
	MagicBytes string = fmt.Sprintf("DCA%d", FormatVersion)

	// 1 for mono, 2 for stereo
	Channels int

	// Must be one of 8000, 12000, 16000, 24000, or 48000.
	// Discord only uses 48000 currently.
	FrameRate int

	// Rates from 500 to 512000 bits per second are meaningful
	// Discord only uses 8000 to 128000 and default is 64000
	Bitrate int

	// Must be one of voip, audio, or lowdelay.
	// DCA defaults to audio which is ideal for music
	// Not sure what Discord uses here, probably voip
	Application string

	FrameSize int // uint16 size of each audio frame
	MaxBytes  int // max size of opus data

	Volume int // change audio volume (256=normal)

	OpusEncoder *gopus.Encoder

	InFile string
	CoverFormat string = "jpeg"

	OutFile string = "pipe:1"
	OutBuf  []byte

	EncodeChan chan []int16
	OutputChan chan []byte

	err error

	wg sync.WaitGroup
)

// init configures and parses the command line arguments
func init() {

	flag.StringVar(&InFile, "i", "pipe:0", "infile")
	flag.IntVar(&Volume, "vol", 256, "change audio volume (256=normal)")
	flag.IntVar(&Channels, "ac", 2, "audio channels")
	flag.IntVar(&FrameRate, "ar", 48000, "audio sampling rate")
	flag.IntVar(&FrameSize, "as", 960, "audio frame size can be 960 (20ms), 1920 (40ms), or 2880 (60ms)")
	flag.IntVar(&Bitrate, "ab", 64, "audio encoding bitrate in kb/s can be 8 - 128")
	flag.StringVar(&Application, "aa", "audio", "audio application can be voip, audio, or lowdelay")
	flag.StringVar(&CoverFormat, "format", "jpeg", "format the cover art will be encoded with")

	if len(os.Args) < 2 {
		flag.Usage()
		os.Exit(1)
	}

	flag.Parse()

	MaxBytes = (FrameSize * Channels) * 2 // max size of opus data
}

// very simple program that wraps ffmpeg and outputs raw opus data frames
// with a uint16 header for each frame with the frame length in bytes
func main() {

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Basic setup and validation
	//////////////////////////////////////////////////////////////////////////

	// If only one argument provided assume it's a filename.
	if len(os.Args) == 2 {
		InFile = os.Args[1]
	}

	// If reading from a file, verify it exists.
	if InFile != "pipe:0" {

		if _, err := os.Stat(InFile); os.IsNotExist(err) {
			fmt.Println("error: infile does not exist")
			flag.Usage()
			return
		}
	}

	// If reading from pipe, make sure pipe is open
	if InFile == "pipe:0" {
		fi, err := os.Stdin.Stat()
		if err != nil {
			fmt.Println(err)
			return
		}

		if (fi.Mode() & os.ModeCharDevice) == 0 {
		} else {
			fmt.Println("error: stdin is not a pipe.")
			flag.Usage()
			return
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Create chans, buffers, and encoder for use
	//////////////////////////////////////////////////////////////////////////

	// create an opusEncoder to use
	OpusEncoder, err = gopus.NewEncoder(FrameRate, Channels, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}

	// set opus encoding options
	//	OpusEncoder.SetVbr(true)                // bool

	if Bitrate < 1 || Bitrate > 512 {
		Bitrate = 64 // Set to Discord default
	}
	OpusEncoder.SetBitrate(Bitrate * 1000)

	switch Application {
	case "voip":
		OpusEncoder.SetApplication(gopus.Voip)
	case "audio":
		OpusEncoder.SetApplication(gopus.Audio)
	case "lowdelay":
		OpusEncoder.SetApplication(gopus.RestrictedLowDelay)
	default:
		OpusEncoder.SetApplication(gopus.Audio)
	}

	OutputChan = make(chan []byte, 10)
	EncodeChan = make(chan []int16, 10)

	// Setup the metadata
	Metadata = MetadataStruct{
		Dca: &DCAMetadata{
			Version: FormatVersion,
			Tool: &DCAToolMetadata{
				Name: "dca",
				Version: ProgramVersion,
				Revision: "",
				Url: GitHubRepositoryURL,
				Author: "bwmarrin",
			},
		},
		SongInfo: &SongMetadata{},
		Origin: &OriginMetadata{},
		Opus: &OpusMetadata{
			Bitrate: Bitrate * 1000,
			SampleRate: FrameRate,
			Application: Application,
			FrameSize: FrameSize,
			Channels: Channels,
		},
	}
	_ = Metadata

	// try get the git revision
	git := exec.Command("cd $GOPATH/src/github.com/bwmarrin/dca && git rev-parse HEAD")
	git.Stdout = &CmdBuf

	err = git.Start()
	if err == nil {
		err = git.Wait()
		if err != nil {
			fmt.Println("Git Error:", err)
			return
		}

		Metadata.Dca.Tool.Revision = CmdBuf.String()
	}

	CmdBuf.Reset()

	// get ffprobe data
	if InFile != "pipe:0" {
		ffprobe := exec.Command("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", InFile)
		ffprobe.Stdout = &CmdBuf

		err = ffprobe.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		err = ffprobe.Wait()
		if err != nil {
			fmt.Println("FFprobe Error:", err)
			return
		}

		err = json.Unmarshal(CmdBuf.Bytes(), &FFprobeData)
		if err != nil {
			fmt.Println("Erorr unmarshaling the FFprobe JSON:", err)
			return
		}

		bitrateInt, err := strconv.Atoi(FFprobeData.Format.Bitrate)
		if err != nil {
			fmt.Println("Could not convert bitrate to int:", err)
			return
		}

		Metadata.SongInfo = &SongMetadata{
			Title: FFprobeData.Format.Tags.Title,
			Artist: FFprobeData.Format.Tags.Artist,
			Album: FFprobeData.Format.Tags.Album,
			Genre: FFprobeData.Format.Tags.Genre,
			Comments: "", // change later?
		}

		Metadata.Origin = &OriginMetadata{
			Source: "file",
			Bitrate: bitrateInt,
			Channels: Channels,
			Encoding: FFprobeData.Format.FormatLongName,
			Url: FFprobeData.Format.FileName,
		}

		CmdBuf.Reset()

		// get cover art
		cover := exec.Command("ffmpeg", "-loglevel", "0", "-i", InFile, "-f", "singlejpeg", "pipe:1")
		cover.Stdout = &CmdBuf

		err = cover.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		err = cover.Wait()
		if err == nil {
			buf := bytes.NewBufferString(CmdBuf.String())

			if CoverFormat == "png" {
				img, err := jpeg.Decode(buf)
				if err == nil { // silently drop it, no image
					err = png.Encode(&PngBuf, img)
					if err == nil {
						Metadata.SongInfo.Cover = base64.StdEncoding.EncodeToString(PngBuf.Bytes())
					}
				}
			} else {
				encodedImage := base64.StdEncoding.EncodeToString(CmdBuf.Bytes())
				Metadata.SongInfo.Cover = encodedImage
			}
		}

		CmdBuf.Reset()
		PngBuf.Reset()
	} else {
		Metadata.Origin = &OriginMetadata{
			Source: "pipe",
			Channels: Channels,
			Encoding: "pcm16/s16le",
			Url: "pipe:0",
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Start reader and writer workers
	//////////////////////////////////////////////////////////////////////////

	wg.Add(1)
	go reader()

	wg.Add(1)
	go encoder()

	wg.Add(1)
	go writer()

	// wait for above goroutines to finish, then exit.
	wg.Wait()
}

// reader reads from the input
func reader() {

	defer func() {
		close(EncodeChan)
		wg.Done()
	}()

	// read from file
	if InFile != "pipe:0" {

		// Create a shell command "object" to run.
		ffmpeg := exec.Command("ffmpeg", "-i", InFile, "-vol", strconv.Itoa(Volume), "-f", "s16le", "-ar", strconv.Itoa(FrameRate), "-ac", strconv.Itoa(Channels), "pipe:1")
		stdout, err := ffmpeg.StdoutPipe()
		if err != nil {
			fmt.Println("StdoutPipe Error:", err)
			return
		}

		// Starts the ffmpeg command
		err = ffmpeg.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		for {

			// read data from ffmpeg stdout
			InBuf := make([]int16, FrameSize*Channels)
			err = binary.Read(stdout, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf

		}
	}

	// read input from stdin pipe
	if InFile == "pipe:0" {

		// 16KB input buffer
		rbuf := bufio.NewReaderSize(os.Stdin, 16384)
		for {

			// read data from stdin
			InBuf := make([]int16, FrameSize*Channels)

			err = binary.Read(rbuf, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf
		}
	}

}

// encoder listens on the EncodeChan and encodes provided PCM16 data
// to opus, then sends the encoded data to the OutputChan
func encoder() {

	defer func() {
		close(OutputChan)
		wg.Done()
	}()

	for {
		pcm, ok := <-EncodeChan
		if !ok {
			// if chan closed, exit
			return
		}

		// try encoding pcm frame with Opus
		opus, err := OpusEncoder.Encode(pcm, FrameSize, MaxBytes)
		if err != nil {
			fmt.Println("Encoding Error:", err)
			return
		}

		// write opus data to OutputChan
		OutputChan <- opus
	}
}

// writer listens on the OutputChan and writes the output to stdout pipe
// TODO: Add support for writing directly to a file
func writer() {

	defer wg.Done()

	var opuslen int16
	var jsonlen int32

	// 16KB output buffer
	wbuf := bufio.NewWriterSize(os.Stdout, 16384)

	// write the magic bytes
	fmt.Print(MagicBytes)

	// encode and write json length
	json, err := json.Marshal(Metadata)
	if err != nil {
		fmt.Println("Failed to encode the Metadata JSON:", err)
		return
	}

	jsonlen = int32(len(json))
	err = binary.Write(wbuf, binary.LittleEndian, &jsonlen)
	if err != nil {
		fmt.Println("error writing output: ", err)
		return
	}

	// write the actual json
	wbuf.Write(json)

	for {
		opus, ok := <-OutputChan
		if !ok {
			// if chan closed, exit
			return
		}

		// write header
		opuslen = int16(len(opus))
		err = binary.Write(wbuf, binary.LittleEndian, &opuslen)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}

		// write opus data to stdout
		err = binary.Write(wbuf, binary.LittleEndian, &opus)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}
	}
}
<commit_msg>remove url<commit_after>package main

import (
	"bytes"
	"bufio"
	"encoding/binary"
	"encoding/json"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"image/png"
	"image/jpeg"

	"github.com/layeh/gopus"
)

// Define constants
const (
	// The current version of the DCA format
	FormatVersion int8 = 1

	// The current version of the DCA program
	ProgramVersion string = "0.0.1"

	// The URL to the GitHub repository of DCA
	GitHubRepositoryURL string = "https://github.com/bwmarrin/dca"
)

// All global variables used within the program
var (
	// Buffer for some commands
	CmdBuf bytes.Buffer
	PngBuf bytes.Buffer

	// Metadata structures
	Metadata	MetadataStruct
	FFprobeData FFprobeMetadata

	// Magic bytes to write at the start of a DCA file
	MagicBytes string = fmt.Sprintf("DCA%d", FormatVersion)

	// 1 for mono, 2 for stereo
	Channels int

	// Must be one of 8000, 12000, 16000, 24000, or 48000.
	// Discord only uses 48000 currently.
	FrameRate int

	// Rates from 500 to 512000 bits per second are meaningful
	// Discord only uses 8000 to 128000 and default is 64000
	Bitrate int

	// Must be one of voip, audio, or lowdelay.
	// DCA defaults to audio which is ideal for music
	// Not sure what Discord uses here, probably voip
	Application string

	FrameSize int // uint16 size of each audio frame
	MaxBytes  int // max size of opus data

	Volume int // change audio volume (256=normal)

	OpusEncoder *gopus.Encoder

	InFile string
	CoverFormat string = "jpeg"

	OutFile string = "pipe:1"
	OutBuf  []byte

	EncodeChan chan []int16
	OutputChan chan []byte

	err error

	wg sync.WaitGroup
)

// init configures and parses the command line arguments
func init() {

	flag.StringVar(&InFile, "i", "pipe:0", "infile")
	flag.IntVar(&Volume, "vol", 256, "change audio volume (256=normal)")
	flag.IntVar(&Channels, "ac", 2, "audio channels")
	flag.IntVar(&FrameRate, "ar", 48000, "audio sampling rate")
	flag.IntVar(&FrameSize, "as", 960, "audio frame size can be 960 (20ms), 1920 (40ms), or 2880 (60ms)")
	flag.IntVar(&Bitrate, "ab", 64, "audio encoding bitrate in kb/s can be 8 - 128")
	flag.StringVar(&Application, "aa", "audio", "audio application can be voip, audio, or lowdelay")
	flag.StringVar(&CoverFormat, "format", "jpeg", "format the cover art will be encoded with")

	if len(os.Args) < 2 {
		flag.Usage()
		os.Exit(1)
	}

	flag.Parse()

	MaxBytes = (FrameSize * Channels) * 2 // max size of opus data
}

// very simple program that wraps ffmpeg and outputs raw opus data frames
// with a uint16 header for each frame with the frame length in bytes
func main() {

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Basic setup and validation
	//////////////////////////////////////////////////////////////////////////

	// If only one argument provided assume it's a filename.
	if len(os.Args) == 2 {
		InFile = os.Args[1]
	}

	// If reading from a file, verify it exists.
	if InFile != "pipe:0" {

		if _, err := os.Stat(InFile); os.IsNotExist(err) {
			fmt.Println("error: infile does not exist")
			flag.Usage()
			return
		}
	}

	// If reading from pipe, make sure pipe is open
	if InFile == "pipe:0" {
		fi, err := os.Stdin.Stat()
		if err != nil {
			fmt.Println(err)
			return
		}

		if (fi.Mode() & os.ModeCharDevice) == 0 {
		} else {
			fmt.Println("error: stdin is not a pipe.")
			flag.Usage()
			return
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Create chans, buffers, and encoder for use
	//////////////////////////////////////////////////////////////////////////

	// create an opusEncoder to use
	OpusEncoder, err = gopus.NewEncoder(FrameRate, Channels, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}

	// set opus encoding options
	//	OpusEncoder.SetVbr(true)                // bool

	if Bitrate < 1 || Bitrate > 512 {
		Bitrate = 64 // Set to Discord default
	}
	OpusEncoder.SetBitrate(Bitrate * 1000)

	switch Application {
	case "voip":
		OpusEncoder.SetApplication(gopus.Voip)
	case "audio":
		OpusEncoder.SetApplication(gopus.Audio)
	case "lowdelay":
		OpusEncoder.SetApplication(gopus.RestrictedLowDelay)
	default:
		OpusEncoder.SetApplication(gopus.Audio)
	}

	OutputChan = make(chan []byte, 10)
	EncodeChan = make(chan []int16, 10)

	// Setup the metadata
	Metadata = MetadataStruct{
		Dca: &DCAMetadata{
			Version: FormatVersion,
			Tool: &DCAToolMetadata{
				Name: "dca",
				Version: ProgramVersion,
				Revision: "",
				Url: GitHubRepositoryURL,
				Author: "bwmarrin",
			},
		},
		SongInfo: &SongMetadata{},
		Origin: &OriginMetadata{},
		Opus: &OpusMetadata{
			Bitrate: Bitrate * 1000,
			SampleRate: FrameRate,
			Application: Application,
			FrameSize: FrameSize,
			Channels: Channels,
		},
	}
	_ = Metadata

	// try get the git revision
	git := exec.Command("cd $GOPATH/src/github.com/bwmarrin/dca && git rev-parse HEAD")
	git.Stdout = &CmdBuf

	err = git.Start()
	if err == nil {
		err = git.Wait()
		if err != nil {
			fmt.Println("Git Error:", err)
			return
		}

		Metadata.Dca.Tool.Revision = CmdBuf.String()
	}

	CmdBuf.Reset()

	// get ffprobe data
	if InFile != "pipe:0" {
		ffprobe := exec.Command("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", InFile)
		ffprobe.Stdout = &CmdBuf

		err = ffprobe.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		err = ffprobe.Wait()
		if err != nil {
			fmt.Println("FFprobe Error:", err)
			return
		}

		err = json.Unmarshal(CmdBuf.Bytes(), &FFprobeData)
		if err != nil {
			fmt.Println("Erorr unmarshaling the FFprobe JSON:", err)
			return
		}

		bitrateInt, err := strconv.Atoi(FFprobeData.Format.Bitrate)
		if err != nil {
			fmt.Println("Could not convert bitrate to int:", err)
			return
		}

		Metadata.SongInfo = &SongMetadata{
			Title: FFprobeData.Format.Tags.Title,
			Artist: FFprobeData.Format.Tags.Artist,
			Album: FFprobeData.Format.Tags.Album,
			Genre: FFprobeData.Format.Tags.Genre,
			Comments: "", // change later?
		}

		Metadata.Origin = &OriginMetadata{
			Source: "file",
			Bitrate: bitrateInt,
			Channels: Channels,
			Encoding: FFprobeData.Format.FormatLongName,
		}

		CmdBuf.Reset()

		// get cover art
		cover := exec.Command("ffmpeg", "-loglevel", "0", "-i", InFile, "-f", "singlejpeg", "pipe:1")
		cover.Stdout = &CmdBuf

		err = cover.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		err = cover.Wait()
		if err == nil {
			buf := bytes.NewBufferString(CmdBuf.String())

			if CoverFormat == "png" {
				img, err := jpeg.Decode(buf)
				if err == nil { // silently drop it, no image
					err = png.Encode(&PngBuf, img)
					if err == nil {
						Metadata.SongInfo.Cover = base64.StdEncoding.EncodeToString(PngBuf.Bytes())
					}
				}
			} else {
				encodedImage := base64.StdEncoding.EncodeToString(CmdBuf.Bytes())
				Metadata.SongInfo.Cover = encodedImage
			}
		}

		CmdBuf.Reset()
		PngBuf.Reset()
	} else {
		Metadata.Origin = &OriginMetadata{
			Source: "pipe",
			Channels: Channels,
			Encoding: "pcm16/s16le",
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Start reader and writer workers
	//////////////////////////////////////////////////////////////////////////

	wg.Add(1)
	go reader()

	wg.Add(1)
	go encoder()

	wg.Add(1)
	go writer()

	// wait for above goroutines to finish, then exit.
	wg.Wait()
}

// reader reads from the input
func reader() {

	defer func() {
		close(EncodeChan)
		wg.Done()
	}()

	// read from file
	if InFile != "pipe:0" {

		// Create a shell command "object" to run.
		ffmpeg := exec.Command("ffmpeg", "-i", InFile, "-vol", strconv.Itoa(Volume), "-f", "s16le", "-ar", strconv.Itoa(FrameRate), "-ac", strconv.Itoa(Channels), "pipe:1")
		stdout, err := ffmpeg.StdoutPipe()
		if err != nil {
			fmt.Println("StdoutPipe Error:", err)
			return
		}

		// Starts the ffmpeg command
		err = ffmpeg.Start()
		if err != nil {
			fmt.Println("RunStart Error:", err)
			return
		}

		for {

			// read data from ffmpeg stdout
			InBuf := make([]int16, FrameSize*Channels)
			err = binary.Read(stdout, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf

		}
	}

	// read input from stdin pipe
	if InFile == "pipe:0" {

		// 16KB input buffer
		rbuf := bufio.NewReaderSize(os.Stdin, 16384)
		for {

			// read data from stdin
			InBuf := make([]int16, FrameSize*Channels)

			err = binary.Read(rbuf, binary.LittleEndian, &InBuf)
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return
			}
			if err != nil {
				fmt.Println("error reading from ffmpeg stdout :", err)
				return
			}

			// write pcm data to the EncodeChan
			EncodeChan <- InBuf
		}
	}

}

// encoder listens on the EncodeChan and encodes provided PCM16 data
// to opus, then sends the encoded data to the OutputChan
func encoder() {

	defer func() {
		close(OutputChan)
		wg.Done()
	}()

	for {
		pcm, ok := <-EncodeChan
		if !ok {
			// if chan closed, exit
			return
		}

		// try encoding pcm frame with Opus
		opus, err := OpusEncoder.Encode(pcm, FrameSize, MaxBytes)
		if err != nil {
			fmt.Println("Encoding Error:", err)
			return
		}

		// write opus data to OutputChan
		OutputChan <- opus
	}
}

// writer listens on the OutputChan and writes the output to stdout pipe
// TODO: Add support for writing directly to a file
func writer() {

	defer wg.Done()

	var opuslen int16
	var jsonlen int32

	// 16KB output buffer
	wbuf := bufio.NewWriterSize(os.Stdout, 16384)

	// write the magic bytes
	fmt.Print(MagicBytes)

	// encode and write json length
	json, err := json.Marshal(Metadata)
	if err != nil {
		fmt.Println("Failed to encode the Metadata JSON:", err)
		return
	}

	jsonlen = int32(len(json))
	err = binary.Write(wbuf, binary.LittleEndian, &jsonlen)
	if err != nil {
		fmt.Println("error writing output: ", err)
		return
	}

	// write the actual json
	wbuf.Write(json)

	for {
		opus, ok := <-OutputChan
		if !ok {
			// if chan closed, exit
			return
		}

		// write header
		opuslen = int16(len(opus))
		err = binary.Write(wbuf, binary.LittleEndian, &opuslen)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}

		// write opus data to stdout
		err = binary.Write(wbuf, binary.LittleEndian, &opus)
		if err != nil {
			fmt.Println("error writing output: ", err)
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
)

//
func main() {
	log.Info("%#v", conf)
	// kill postgres server thats running
	log.Info("killing old postgres if there is one")
	killOldPostgres()

	handle(ClusterStart())
	handle(StatusStart())
	handle(DecisionStart())
	handle(ActionStart())

	// signal Handle
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, os.Kill, syscall.SIGQUIT, syscall.SIGHUP)

	// Block until a signal is received.
	for {
		s := <-c
		switch s {
		case syscall.SIGINT, os.Kill, syscall.SIGQUIT, syscall.SIGTERM:
			// kill the database then quit
			log.Info("Signal Recieved: %s", s.String())
			if conf.Role == "monitor" {
				log.Info("shutting down")
			} else {
				log.Info("Killing Database")
				actions <- "kill"
				// called twice because the first call returns when the job is picked up
				// the second call returns when the first job is complete
				actions <- "kill"
			}
			log.Close()
			os.Exit(0)
		case syscall.SIGHUP:
			// demote
			log.Info("Signal Recieved: %s", s.String())
			log.Info("advising a demotion")
			advice <- "demote"
		}
	}

}

//
func handle(err error) {
	if err != nil {
		fmt.Println("error: " + err.Error())
		os.Exit(1)
	}
}

func killOldPostgres() {
	killOld := exec.Command("pg_ctl", "stop", "-D", conf.DataDir, "-m", "fast")
	killOld.Stdout = Piper{"[KillOLD.stdout]"}
	killOld.Stderr = Piper{"[KillOLD.stderr]"}
	if err := killOld.Run(); err != nil {
		log.Error("[action] KillOLD failed.")
	}
}
<commit_msg>update<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
)

//
func main() {
	log.Info("%#v", conf)
	// kill postgres server thats running
	log.Info("killing old postgres if there is one")
	killOldPostgres()

	handle(ClusterStart())
	handle(StatusStart())
	handle(DecisionStart())
	handle(ActionStart())

	// signal Handle
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, os.Kill, syscall.SIGQUIT, syscall.SIGHUP, syscall.SIGPOLL)

	// Block until a signal is received.
	for {
		s := <-c
		switch s {
		case syscall.SIGINT, os.Kill, syscall.SIGQUIT, syscall.SIGTERM:
			// kill the database then quit
			log.Info("Signal Recieved: %s", s.String())
			if conf.Role == "monitor" {
				log.Info("shutting down")
			} else {
				log.Info("Killing Database")
				actions <- "kill"
				// called twice because the first call returns when the job is picked up
				// the second call returns when the first job is complete
				actions <- "kill"
			}
			log.Close()
			os.Exit(0)
		case syscall.SIGPOLL:
			stacktrace := make([]byte, 8192)
			length := runtime.Stack(stacktrace, true)
			fmt.Println(string(stacktrace[:length]))
		case syscall.SIGHUP:
			// demote
			log.Info("Signal Recieved: %s", s.String())
			log.Info("advising a demotion")
			advice <- "demote"
		}
	}

}

//
func handle(err error) {
	if err != nil {
		fmt.Println("error: " + err.Error())
		os.Exit(1)
	}
}

func killOldPostgres() {
	killOld := exec.Command("pg_ctl", "stop", "-D", conf.DataDir, "-m", "fast")
	killOld.Stdout = Piper{"[KillOLD.stdout]"}
	killOld.Stderr = Piper{"[KillOLD.stderr]"}
	if err := killOld.Run(); err != nil {
		log.Error("[action] KillOLD failed.")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	md5 "crypto/md5"
	"fmt"
	"github.com/dustin/go-humanize"
	"io"
	"log"
	"os"
	filepath "path/filepath"
	"sort"
	"strconv"
	"strings"
)

type info struct {
	Size  uint64
	Names []string
}

type bySize []info

func (a bySize) Len() int           { return len(a) }
func (a bySize) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a bySize) Less(i, j int) bool { return a[i].Size < a[j].Size }

func getInput(prompt string, validator func(string) bool) (string, error) {
	// Reader to read from user input.
	reader := bufio.NewReader(os.Stdin)
	var (
		val string
		err error
	)
	for {
		fmt.Printf(prompt)
		val, err = reader.ReadString('\n')
		if err != nil {
			return "", err
		}
		val = strings.TrimSpace(val)
		if validator(val) {
			break
		}
	}
	return val, nil
}

func main() {
	// Get parent dir.
	root, err := getInput("Enter parent directory to scan for duplicates in: ", func(f string) bool {
		i, err := os.Stat(f)
		if err != nil {
			log.Print(err)
			return false
		}
		return i.IsDir()
	})
	if err != nil {
		log.Fatal(err)
	}
	if !os.IsPathSeparator(root[len(root)-1]) {
		root = fmt.Sprintf("%s%c", root, os.PathSeparator)
	}
	// Get files.
	files := make(map[uint64]map[[md5.Size]byte][]string) // Size -> ( hash -> [name] )
	ps := []rune{'|', '/', '-', '\\', '|', '/', '-', '\\'}
	i := 0
	fmt.Printf("Indexing %s \n \r", root)
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		i++
		fmt.Printf("%c \r", ps[(i%100)%len(ps)])
		hs, ok := files[uint64(info.Size())]
		if !ok {
			hs = make(map[[md5.Size]byte][]string)
			files[uint64(info.Size())] = hs
		}
		f, err := os.Open(path)
		if err != nil {
			log.Print(err)
			return err
		}
		defer f.Close()
		h := md5.New()
		_, err = io.Copy(h, f)
		if err != nil {
			log.Print(err)
			return err
		}
		var sum [md5.Size]byte
		s := h.Sum(nil)
		if len(s) != len(sum) {
			panic("Unexpected checksum length")
		}
		for i, v := range s {
			sum[i] = v
		}
		fs, ok := hs[sum]
		if !ok {
			hs[sum] = []string{path}
		} else {
			hs[sum] = append(fs, path)
		}

		return nil
	})
	dupes := []info{}
	fcount := 0
	tsize := uint64(0)
	for size, hashes := range files {
		for _, files := range hashes {
			if len(files) > 1 {
				dupes = append(dupes, info{Size: size, Names: files})
				fcount += len(files)
				tsize += size
			}
		}
	}
	fmt.Printf("Found %d sets of duplicate files\n", len(dupes))
	fmt.Printf("Total file count: %d\n", fcount)
	fmt.Printf("Total size used: %d\n", tsize)

	sort.Sort(sort.Reverse(bySize(dupes)))

	fmt.Printf("\nReviewing results: for each duplicate fileset, select 'f' to delete all but the first file, 'a' to keep all files, or 'n' followed by a file number (e.g. n2) to delete all except the second file.\n")

	for _, dupe := range dupes {
		keep := -1
		_, err := getInput(fmt.Sprintf("%s: %v\nKeep [F]irst/[a]ll/[n]th? ", humanize.Bytes(dupe.Size*uint64(len(dupe.Names)-1)), dupe.Names), func(v string) bool {
			switch strings.ToLower(v) {
			case "f":
				keep = 0
				return true
			case "a":
				return true
			default:
				if strings.HasPrefix(strings.ToLower(v), "n") {
					k, err := strconv.ParseInt(v[1:], 10, 32)
					if err != nil {
						return false
					}
					if k < 1 || int(k) > len(dupe.Names) {
						return false
					}
					keep = int(k) - 1
					return true
				} else {
					return false
				}
			}
			return false
		})
		if err != nil {
			log.Fatal(err)
		}
		if keep >= 0 {
			for i, n := range dupe.Names {
				if i != keep {
					if err := os.Remove(n); err != nil {
						log.Printf("Error deleting %n: %v", n, err)
					}
				}
			}
		}
	}
}
<commit_msg>Improve layout. Efficient search.<commit_after>package main

import (
	"bufio"
	md5 "crypto/md5"
	"fmt"
	"github.com/dustin/go-humanize"
	"io"
	"log"
	"os"
	filepath "path/filepath"
	"sort"
	"strconv"
	"strings"
)

type filesWithHashes struct {
	Unhashed string
	Hashes   map[[md5.Size]byte][]string
}

type info struct {
	Size  uint64
	Names []string
}

type bySize []info

func (a bySize) Len() int           { return len(a) }
func (a bySize) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a bySize) Less(i, j int) bool { return a[i].Size < a[j].Size }

func getInput(prompt string, validator func(string) bool) (string, error) {
	// Reader to read from user input.
	reader := bufio.NewReader(os.Stdin)
	var (
		val string
		err error
	)
	for {
		fmt.Printf(prompt)
		val, err = reader.ReadString('\n')
		if err != nil {
			return "", err
		}
		val = strings.TrimSpace(val)
		if validator(val) {
			break
		}
	}
	return val, nil
}

func hash(path string) ([md5.Size]byte, error) {
	var sum [md5.Size]byte
	f, err := os.Open(path)
	if err != nil {
		log.Print(err)
		return sum, err
	}
	defer f.Close()
	h := md5.New()
	_, err = io.Copy(h, f)
	if err != nil {
		log.Print(err)
		return sum, err
	}
	s := h.Sum(nil)
	if len(s) != len(sum) {
		panic("Unexpected checksum length")
	}
	for i, v := range s {
		sum[i] = v
	}
	return sum, nil
}

func main() {
	// Get parent dir.
	root, err := getInput("Enter parent directory to scan for duplicates in: ", func(f string) bool {
		i, err := os.Stat(f)
		if err != nil {
			log.Print(err)
			return false
		}
		return i.IsDir()
	})
	if err != nil {
		log.Fatal(err)
	}
	if !os.IsPathSeparator(root[len(root)-1]) {
		root = fmt.Sprintf("%s%c", root, os.PathSeparator)
	}
	// Get files.
	files := make(map[uint64]*filesWithHashes)
	ps := []rune{'|', '/', '-', '\\', '|', '/', '-', '\\'}
	i := 0
	fmt.Printf("Indexing %s \n \r", root)
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		i++
		fmt.Printf("%c \r", ps[(int(i/30))%len(ps)])
		hs, ok := files[uint64(info.Size())]
		if !ok {
			// If we've never seen another file of this size, we don't have to do the md5 sum.
			files[uint64(info.Size())] = &filesWithHashes{
				Unhashed: path,
				Hashes:   make(map[[md5.Size]byte][]string),
			}
			return nil
		}
		// Add the hash of this file.
		sum, err := hash(path)
		if err != nil {
			return err
		}
		fs, ok := hs.Hashes[sum]
		if !ok {
			hs.Hashes[sum] = []string{path}
		} else {
			hs.Hashes[sum] = append(fs, path)
		}
		// And check if there is also an unhashed file.
		if hs.Unhashed != "" {
			sum, err := hash(hs.Unhashed)
			if err != nil {
				return err
			}
			fs, ok := hs.Hashes[sum]
			if !ok {
				hs.Hashes[sum] = []string{hs.Unhashed}
			} else {
				hs.Hashes[sum] = append(fs, hs.Unhashed)
			}
			hs.Unhashed = ""
		}
		return nil
	})
	dupes := []info{}
	fcount := 0
	tsize := uint64(0)
	for size, hs := range files {
		for _, files := range hs.Hashes {
			if len(files) > 1 {
				dupes = append(dupes, info{Size: size, Names: files})
				fcount += len(files)
				tsize += size
			}
		}
	}
	fmt.Printf("Found %d sets of duplicate files\n", len(dupes))
	fmt.Printf("Total file count: %d\n", fcount)
	fmt.Printf("Total size used: %d\n", tsize)

	sort.Sort(sort.Reverse(bySize(dupes)))

	fmt.Printf("\nReviewing results:\nFor each duplicate fileset, select 'f' to delete all but the first file, 'a' to keep all files, or 'n' (e.g. 2) to delete all except the second file.\n")

	for _, dupe := range dupes {
		keep := -1
		names := ""
		for i, n := range dupe.Names {
			names += fmt.Sprintf("%d: %s\n", i+1, n)
		}
		_, err := getInput(fmt.Sprintf("%s:\n%s\nKeep [F]irst/[a]ll/[n]th? ", humanize.Bytes(dupe.Size*uint64(len(dupe.Names)-1)), names), func(v string) bool {
			switch strings.ToLower(v) {
			case "f":
				keep = 0
				return true
			case "a":
				return true
			default:
				k, err := strconv.ParseInt(v, 10, 32)
				if err != nil {
					return false
				}
				if k < 1 || int(k) > len(dupe.Names) {
					return false
				}
				keep = int(k) - 1
				return true
			}
		})
		if err != nil {
			log.Fatal(err)
		}
		if keep >= 0 {
			for i, n := range dupe.Names {
				if i != keep {
					if err := os.Remove(n); err != nil {
						log.Printf("Error deleting %n: %v", n, err)
					}
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/api/types/strslice"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"

	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"
)

/* DOCS:
1. https://godoc.org/github.com/moby/moby/client
2. https://docs.docker.com/engine/api/v1.31/
*/

type buildstruct struct {
	// remember to use caps so that they can be exported
	Context    string `yaml:"context,omitempty"`
	Dockerfile string `yaml:"dockerfile,omitempty"`
}

type serviceConfig struct {
	Image       string      `yaml:"image,omitempty"`
	Ports       []string    `yaml:"ports,omitempty"`
	Labels      []string    `yaml:"labels,omitempty"`
	Environment []string    `yaml:"environment,omitempty"`
	Command     string      `yaml:"command,flow,omitempty"`
	Restart     string      `yaml:"restart,omitempty"`
	Build       buildstruct `yaml:"build,omitempty"`
	//Volumes     []string `yaml:"volumes,omitempty"`
	//VolumesFrom []string `yaml:"volumes_from,omitempty"`
	//Links          yaml.MaporColonSlice `yaml:"links,omitempty"`
}

type dockerComposeConfig struct {
	Version  string                   `yaml:"version,omitempty"`
	Services map[string]serviceConfig `yaml:"services"`
	//networks map[string]     `yaml:"networks,omitempty"`
	//volumes map[string]                  `yaml:"volumes,omitempty"`
}

func main() {
	data, err := ioutil.ReadFile("docker-compose.yml")
	if err != nil {
		log.Fatal(errors.Wrap(err, "unable to read docker-compose file"))
	}
	curentDir, err := os.Getwd()
	if err != nil {
		log.Fatal(err, "unable to get the current working directory")
	}
	networkName := "meli_network_" + getCwdName(curentDir)
	networkID, err := getNetwork(networkName)
	if err != nil {
		log.Fatal(err, "unable to create/get network")
	}

	var dockerCyaml dockerComposeConfig
	err = yaml.Unmarshal([]byte(data), &dockerCyaml)
	if err != nil {
		log.Fatal(errors.Wrap(err, "unable to parse docker-compose file contents"))
	}

	var wg sync.WaitGroup
	for _, v := range dockerCyaml.Services {
		wg.Add(1)
		fmt.Println("docker service", v)
		go fakepullImage(v, networkID, networkName, &wg)
		//go pullImage(v, networkID, networkName, &wg)
	}
	wg.Wait()
}

func fakepullImage(s serviceConfig, networkName, networkID string, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println()

	if s.Build != (buildstruct{}) {
		fmt.Printf("%+v\n", s)

		ctx := context.Background()
		cli, err := client.NewEnvClient()
		if err != nil {
			log.Fatal(errors.Wrap(err, "fakepullImage:: unable to intialize docker client"))
		}

		dockerContext, err := os.Open(s.Build.Dockerfile)
		if err != nil {
			log.Fatal(errors.Wrap(err, "fakepullImage:: unable to open Dockerfile"))
		}
		defer dockerContext.Close()
		// b := make([]byte, 100)
		// dockerContext.Read(b)
		// fmt.Println("b::::", string(b))
		//dockerContext := bytes.NewReader([]byte(s.Build.Context)) //strings.NewReader(s.Build.Context) bytes.NewReader([]byte)
		imageBuildResponse, err := cli.ImageBuild(ctx, dockerContext, types.ImageBuildOptions{
			//PullParent:     true,
			//Squash:     true, currently only supported in experimenta mode
			Remove:         true, //remove intermediary containers after build
			ForceRemove:    true,
			SuppressOutput: false,
			Dockerfile:     s.Build.Dockerfile,
			Context:        dockerContext,
		})
		if err != nil {
			log.Fatal(errors.Wrap(err, "fakepullImage:: unable to build docker image"))
		}
		fmt.Println("imageBuildResponse::", imageBuildResponse)

	}

}

func pullImage(s serviceConfig, networkID, networkName string, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println()
	fmt.Println("docker servie:", s)
	fmt.Println()
	formattedImageName := fomatImageName(s.Image)
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Fatal(errors.Wrap(err, "unable to intialize docker client"))
	}

	// 1. Pull Image
	imagePullResp, err := cli.ImagePull(
		ctx,
		s.Image,
		types.ImagePullOptions{})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to pull image"))
	}
	defer imagePullResp.Close()
	_, err = io.Copy(os.Stdout, imagePullResp)
	if err != nil {
		log.Println(errors.Wrap(err, "unable to write to stdout"))
	}

	// 2. Create a container
	// 2.1 make labels
	labelsMap := make(map[string]string)
	if len(s.Labels) > 0 {
		for _, v := range s.Labels {
			onelabel := fomatLabels(v)
			labelsMap[onelabel[0]] = onelabel[1]
			fmt.Println("labelsMap", labelsMap)
		}
	}
	//2.2 make ports
	portsMap := make(map[nat.Port]struct{})
	type emptyStruct struct{}
	portBindingMap := make(map[nat.Port][]nat.PortBinding)
	if len(s.Ports) > 0 {
		for _, v := range s.Ports {
			oneport := fomatPorts(v)
			hostport := oneport[0]
			containerport := oneport[1]
			port, err := nat.NewPort("tcp", containerport)
			myPortBinding := nat.PortBinding{HostPort: hostport}
			if err != nil {
				log.Println(errors.Wrap(err, "unable to create a nat.Port"))
			}
			portsMap[port] = emptyStruct{}
			portBindingMap[port] = []nat.PortBinding{myPortBinding}
		}
	}
	//2.3 create command
	cmd := strslice.StrSlice{}
	if s.Command != "" {
		sliceCommand := strings.Fields(s.Command)
		cmd = strslice.StrSlice(sliceCommand)
	}
	//2.4 create restart policy
	restartPolicy := container.RestartPolicy{}
	if s.Restart != "" {
		// you cannot set MaximumRetryCount for the following restart policies;
		// always, no, unless-stopped
		if s.Restart == "on-failure" {
			restartPolicy = container.RestartPolicy{Name: s.Restart, MaximumRetryCount: 3}
		} else {
			restartPolicy = container.RestartPolicy{Name: s.Restart}
		}

	}

	// TODO: we should skip creating the container again if already exists
	// instead of creating a uniquely named container name
	containerCreateResp, err := cli.ContainerCreate(
		ctx,
		&container.Config{
			Image:        s.Image,
			Labels:       labelsMap,
			Env:          s.Environment,
			ExposedPorts: portsMap,
			Cmd:          cmd},
		&container.HostConfig{
			PublishAllPorts: false,
			PortBindings:    portBindingMap,
			NetworkMode:     container.NetworkMode(networkName),
			RestartPolicy:   restartPolicy},
		nil,
		formattedImageName)
	if err != nil {
		log.Println(errors.Wrap(err, "unable to create container"))
	}

	// 3. Connect container to network
	err = cli.NetworkConnect(
		ctx,
		networkID,
		containerCreateResp.ID,
		&network.EndpointSettings{})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to connect container to network"))
	}

	// 4. Start container
	err = cli.ContainerStart(
		ctx,
		containerCreateResp.ID,
		types.ContainerStartOptions{})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to start container"))
	}

	// 5. Stream container logs to stdOut
	containerLogResp, err := cli.ContainerLogs(
		ctx,
		containerCreateResp.ID,
		types.ContainerLogsOptions{
			ShowStdout: true,
			ShowStderr: true,
			Timestamps: true})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to get container logs"))
	}
	defer containerLogResp.Close()
	_, err = io.Copy(os.Stdout, containerLogResp)
	if err != nil {
		log.Println(errors.Wrap(err, "unable to write to stdout"))
	}
}
<commit_msg>try building image<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/api/types/strslice"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"

	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"
)

/* DOCS:
1. https://godoc.org/github.com/moby/moby/client
2. https://docs.docker.com/engine/api/v1.31/
*/

type buildstruct struct {
	// remember to use caps so that they can be exported
	Context    string `yaml:"context,omitempty"`
	Dockerfile string `yaml:"dockerfile,omitempty"`
}

type serviceConfig struct {
	Image       string      `yaml:"image,omitempty"`
	Ports       []string    `yaml:"ports,omitempty"`
	Labels      []string    `yaml:"labels,omitempty"`
	Environment []string    `yaml:"environment,omitempty"`
	Command     string      `yaml:"command,flow,omitempty"`
	Restart     string      `yaml:"restart,omitempty"`
	Build       buildstruct `yaml:"build,omitempty"`
	//Volumes     []string `yaml:"volumes,omitempty"`
	//VolumesFrom []string `yaml:"volumes_from,omitempty"`
	//Links          yaml.MaporColonSlice `yaml:"links,omitempty"`
}

type dockerComposeConfig struct {
	Version  string                   `yaml:"version,omitempty"`
	Services map[string]serviceConfig `yaml:"services"`
	//networks map[string]     `yaml:"networks,omitempty"`
	//volumes map[string]                  `yaml:"volumes,omitempty"`
}

func main() {
	data, err := ioutil.ReadFile("docker-compose.yml")
	if err != nil {
		log.Fatal(errors.Wrap(err, "unable to read docker-compose file"))
	}
	curentDir, err := os.Getwd()
	if err != nil {
		log.Fatal(err, "unable to get the current working directory")
	}
	networkName := "meli_network_" + getCwdName(curentDir)
	networkID, err := getNetwork(networkName)
	if err != nil {
		log.Fatal(err, "unable to create/get network")
	}

	var dockerCyaml dockerComposeConfig
	err = yaml.Unmarshal([]byte(data), &dockerCyaml)
	if err != nil {
		log.Fatal(errors.Wrap(err, "unable to parse docker-compose file contents"))
	}

	var wg sync.WaitGroup
	for _, v := range dockerCyaml.Services {
		wg.Add(1)
		fmt.Println("docker service", v)
		go fakepullImage(v, networkID, networkName, &wg)
		//go pullImage(v, networkID, networkName, &wg)
	}
	wg.Wait()
}

func fakepullImage(s serviceConfig, networkName, networkID string, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println()

	if s.Build != (buildstruct{}) {
		fmt.Printf("%+v\n", s)

		ctx := context.Background()
		cli, err := client.NewEnvClient()
		if err != nil {
			log.Fatal(errors.Wrap(err, "fakepullImage:: unable to intialize docker client"))
		}

		dockerContext, err := os.Open(s.Build.Dockerfile)
		if err != nil {
			log.Fatal(errors.Wrap(err, "fakepullImage:: unable to open Dockerfile"))
		}
		defer dockerContext.Close()
		// b := make([]byte, 100)
		// dockerContext.Read(b)
		// fmt.Println("b::::", string(b))
		// dont call dockerContext.Read(), otherwise cli.ImageBuild will fail with EOF
		// you cant read twice

		//dockerContext := bytes.NewReader([]byte(s.Build.Context)) //strings.NewReader(s.Build.Context) bytes.NewReader([]byte)
		imageBuildResponse, err := cli.ImageBuild(ctx, dockerContext, types.ImageBuildOptions{
			//PullParent:     true,
			//Squash:     true, currently only supported in experimenta mode
			Remove:         true, //remove intermediary containers after build
			ForceRemove:    true,
			SuppressOutput: false,
			Dockerfile:     s.Build.Dockerfile,
			Context:        dockerContext,
		})
		if err != nil {
			log.Fatal(errors.Wrap(err, "fakepullImage:: unable to build docker image"))
		}
		fmt.Println("imageBuildResponse::", imageBuildResponse)

	}

}

func pullImage(s serviceConfig, networkID, networkName string, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println()
	fmt.Println("docker servie:", s)
	fmt.Println()
	formattedImageName := fomatImageName(s.Image)
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Fatal(errors.Wrap(err, "unable to intialize docker client"))
	}

	// 1. Pull Image
	imagePullResp, err := cli.ImagePull(
		ctx,
		s.Image,
		types.ImagePullOptions{})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to pull image"))
	}
	defer imagePullResp.Close()
	_, err = io.Copy(os.Stdout, imagePullResp)
	if err != nil {
		log.Println(errors.Wrap(err, "unable to write to stdout"))
	}

	// 2. Create a container
	// 2.1 make labels
	labelsMap := make(map[string]string)
	if len(s.Labels) > 0 {
		for _, v := range s.Labels {
			onelabel := fomatLabels(v)
			labelsMap[onelabel[0]] = onelabel[1]
			fmt.Println("labelsMap", labelsMap)
		}
	}
	//2.2 make ports
	portsMap := make(map[nat.Port]struct{})
	type emptyStruct struct{}
	portBindingMap := make(map[nat.Port][]nat.PortBinding)
	if len(s.Ports) > 0 {
		for _, v := range s.Ports {
			oneport := fomatPorts(v)
			hostport := oneport[0]
			containerport := oneport[1]
			port, err := nat.NewPort("tcp", containerport)
			myPortBinding := nat.PortBinding{HostPort: hostport}
			if err != nil {
				log.Println(errors.Wrap(err, "unable to create a nat.Port"))
			}
			portsMap[port] = emptyStruct{}
			portBindingMap[port] = []nat.PortBinding{myPortBinding}
		}
	}
	//2.3 create command
	cmd := strslice.StrSlice{}
	if s.Command != "" {
		sliceCommand := strings.Fields(s.Command)
		cmd = strslice.StrSlice(sliceCommand)
	}
	//2.4 create restart policy
	restartPolicy := container.RestartPolicy{}
	if s.Restart != "" {
		// you cannot set MaximumRetryCount for the following restart policies;
		// always, no, unless-stopped
		if s.Restart == "on-failure" {
			restartPolicy = container.RestartPolicy{Name: s.Restart, MaximumRetryCount: 3}
		} else {
			restartPolicy = container.RestartPolicy{Name: s.Restart}
		}

	}

	// TODO: we should skip creating the container again if already exists
	// instead of creating a uniquely named container name
	containerCreateResp, err := cli.ContainerCreate(
		ctx,
		&container.Config{
			Image:        s.Image,
			Labels:       labelsMap,
			Env:          s.Environment,
			ExposedPorts: portsMap,
			Cmd:          cmd},
		&container.HostConfig{
			PublishAllPorts: false,
			PortBindings:    portBindingMap,
			NetworkMode:     container.NetworkMode(networkName),
			RestartPolicy:   restartPolicy},
		nil,
		formattedImageName)
	if err != nil {
		log.Println(errors.Wrap(err, "unable to create container"))
	}

	// 3. Connect container to network
	err = cli.NetworkConnect(
		ctx,
		networkID,
		containerCreateResp.ID,
		&network.EndpointSettings{})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to connect container to network"))
	}

	// 4. Start container
	err = cli.ContainerStart(
		ctx,
		containerCreateResp.ID,
		types.ContainerStartOptions{})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to start container"))
	}

	// 5. Stream container logs to stdOut
	containerLogResp, err := cli.ContainerLogs(
		ctx,
		containerCreateResp.ID,
		types.ContainerLogsOptions{
			ShowStdout: true,
			ShowStderr: true,
			Timestamps: true})
	if err != nil {
		log.Println(errors.Wrap(err, "unable to get container logs"))
	}
	defer containerLogResp.Close()
	_, err = io.Copy(os.Stdout, containerLogResp)
	if err != nil {
		log.Println(errors.Wrap(err, "unable to write to stdout"))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"time"

	"github.com/apcera/termtables"
)

const DEFAULT_DB_NAME = "focus.db"

func getDBPath() (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", err
	}
	s := u.HomeDir + "/" + DEFAULT_DB_NAME
	return s, err
}

func usage(err string) {
	fmt.Println("Commands: start, stop, list, today, yesterday")
	fmt.Println("Error: " + err)
}

func exit(code int) {
	os.Exit(code)
}

func cmdYesterday(s Store) error {
	yesterday := time.Now().AddDate(0, 0, -1)
	results, err := s.ListDay(yesterday)
	if err != nil {
		return err
	}
	printElements(results)
	return nil
}

func cmdToday(s Store) error {
	today := time.Now()
	results, err := s.ListDay(today)
	if err != nil {
		return err
	}
	printElements(results)
	return nil
}

func cmdNow(s Store) error {
	result, err := s.Now()
	if err != nil && err != ErrNoElement {
		return err
	}
	if err != ErrNoElement {
		fmt.Println(result.Name)
	}
	return nil
}

func cmdList(s Store) error {
	results, err := s.List()
	if err != nil {
		return err
	}
	printElements(results)
	return nil
}

func cmdStop(s Store) error {
	return s.Stop()
}

func printElements(elements []Element) {
	table := termtables.CreateTable()
	table.AddHeaders("Name", "Duration")
	var td time.Duration

	for _, r := range elements {
		d := r.Duration()
		var tag string
		if r.IsRunning() {
			tag = " (R)"
		}
		table.AddRow(r.Name+tag, d)
		td += d
	}
	fmt.Println(table.Render())
	fmt.Println("Total duration:", td)

}
func main() {
	if len(os.Args) < 2 {
		usage("Missing cmd")
		exit(1)
	}
	path, err := getDBPath()
	if err != nil {
		log.Fatal(err)
	}
	fs, err := NewSQLiteStore(path)
	if err != nil {
		log.Fatal(err)
	}
	cmd := os.Args[1]
	switch cmd {
	case "start":
		name := os.Args[2]
		err = fs.Start(name)
	case "list":
		err = cmdList(fs)
	case "today":
		err = cmdToday(fs)
	case "yesterday":
		err = cmdYesterday(fs)
	case "now":
		err = cmdNow(fs)
	case "stop":
		err = cmdStop(fs)
	default:
		usage("Command not found: " + cmd)
		exit(2)
	}
	if err != nil {
		usage(err.Error())
		exit(3)
	}
}
<commit_msg>Default DB Path changed to be hidden<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"time"

	"github.com/apcera/termtables"
)

const DEFAULT_DB_NAME = ".focus.db"

func getDBPath() (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", err
	}
	s := u.HomeDir + "/" + DEFAULT_DB_NAME
	return s, err
}

func usage(err string) {
	fmt.Println("Commands: start, stop, list, today, yesterday")
	fmt.Println("Error: " + err)
}

func exit(code int) {
	os.Exit(code)
}

func cmdYesterday(s Store) error {
	yesterday := time.Now().AddDate(0, 0, -1)
	results, err := s.ListDay(yesterday)
	if err != nil {
		return err
	}
	printElements(results)
	return nil
}

func cmdToday(s Store) error {
	today := time.Now()
	results, err := s.ListDay(today)
	if err != nil {
		return err
	}
	printElements(results)
	return nil
}

func cmdNow(s Store) error {
	result, err := s.Now()
	if err != nil && err != ErrNoElement {
		return err
	}
	if err != ErrNoElement {
		fmt.Println(result.Name)
	}
	return nil
}

func cmdList(s Store) error {
	results, err := s.List()
	if err != nil {
		return err
	}
	printElements(results)
	return nil
}

func cmdStop(s Store) error {
	return s.Stop()
}

func printElements(elements []Element) {
	table := termtables.CreateTable()
	table.AddHeaders("Name", "Duration")
	var td time.Duration

	for _, r := range elements {
		d := r.Duration()
		var tag string
		if r.IsRunning() {
			tag = " (R)"
		}
		table.AddRow(r.Name+tag, d)
		td += d
	}
	fmt.Println(table.Render())
	fmt.Println("Total duration:", td)

}
func main() {
	if len(os.Args) < 2 {
		usage("Missing cmd")
		exit(1)
	}
	path, err := getDBPath()
	if err != nil {
		log.Fatal(err)
	}
	fs, err := NewSQLiteStore(path)
	if err != nil {
		log.Fatal(err)
	}
	cmd := os.Args[1]
	switch cmd {
	case "start":
		name := os.Args[2]
		err = fs.Start(name)
	case "list":
		err = cmdList(fs)
	case "today":
		err = cmdToday(fs)
	case "yesterday":
		err = cmdYesterday(fs)
	case "now":
		err = cmdNow(fs)
	case "stop":
		err = cmdStop(fs)
	default:
		usage("Command not found: " + cmd)
		exit(2)
	}
	if err != nil {
		usage(err.Error())
		exit(3)
	}
}
<|endoftext|>