<commit_before>package main

import (
	"context"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"

	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/linki/chaoskube/chaoskube"
	"github.com/linki/chaoskube/terminator"
	"github.com/linki/chaoskube/util"
)

var (
	version = "undefined"
)

var (
	labelString        string
	annString          string
	nsString           string
	excludedWeekdays   string
	excludedTimesOfDay string
	excludedDaysOfYear string
	timezone           string
	minimumAge         time.Duration
	master             string
	kubeconfig         string
	interval           time.Duration
	dryRun             bool
	debug              bool
	metricsAddress     string
	gracePeriod        time.Duration
	logFormat          string
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())

	kingpin.Flag("labels", "A set of labels to restrict the list of affected pods. Defaults to everything.").StringVar(&labelString)
	kingpin.Flag("annotations", "A set of annotations to restrict the list of affected pods. Defaults to everything.").StringVar(&annString)
	kingpin.Flag("namespaces", "A set of namespaces to restrict the list of affected pods. Defaults to everything.").StringVar(&nsString)
	kingpin.Flag("excluded-weekdays", "A list of weekdays when termination is suspended, e.g. Sat,Sun").StringVar(&excludedWeekdays)
	kingpin.Flag("excluded-times-of-day", "A list of time periods of a day when termination is suspended, e.g. 22:00-08:00").StringVar(&excludedTimesOfDay)
	kingpin.Flag("excluded-days-of-year", "A list of days of a year when termination is suspended, e.g. Apr1,Dec24").StringVar(&excludedDaysOfYear)
	kingpin.Flag("timezone", "The timezone by which to interpret the excluded weekdays and times of day, e.g. UTC, Local, Europe/Berlin. Defaults to UTC.").Default("UTC").StringVar(&timezone)
	kingpin.Flag("minimum-age", "Minimum age of pods to consider for termination").Default("0s").DurationVar(&minimumAge)
	kingpin.Flag("master", "The address of the Kubernetes cluster to target").StringVar(&master)
	kingpin.Flag("kubeconfig", "Path to a kubeconfig file").StringVar(&kubeconfig)
	kingpin.Flag("interval", "Interval between Pod terminations").Default("10m").DurationVar(&interval)
	kingpin.Flag("dry-run", "If true, don't actually do anything.").Default("true").BoolVar(&dryRun)
	kingpin.Flag("debug", "Enable debug logging.").BoolVar(&debug)
	kingpin.Flag("metrics-address", "Listening address for metrics handler").Default(":8080").StringVar(&metricsAddress)
	kingpin.Flag("grace-period", "Grace period to terminate Pods. Negative values will use the Pod's grace period.").Default("-1s").DurationVar(&gracePeriod)
	kingpin.Flag("log-format", "Select log format options").Default("text").EnumVar(&logFormat, "text", "json")
}

func main() {
	kingpin.Version(version)
	kingpin.Parse()

	if debug {
		log.SetLevel(log.DebugLevel)
	}

	if logFormat == "json" {
		log.SetFormatter(&log.JSONFormatter{})
	}

	log.WithFields(log.Fields{
		"labels":             labelString,
		"annotations":        annString,
		"namespaces":         nsString,
		"excludedWeekdays":   excludedWeekdays,
		"excludedTimesOfDay": excludedTimesOfDay,
		"excludedDaysOfYear": excludedDaysOfYear,
		"timezone":           timezone,
		"minimumAge":         minimumAge,
		"master":             master,
		"kubeconfig":         kubeconfig,
		"interval":           interval,
		"dryRun":             dryRun,
		"debug":              debug,
		"metricsAddress":     metricsAddress,
		"gracePeriod":        gracePeriod,
	}).Debug("reading config")

	log.WithFields(log.Fields{
		"version":  version,
		"dryRun":   dryRun,
		"interval": interval,
	}).Info("starting up")

	client, err := newClient()
	if err != nil {
		log.WithField("err", err).Fatal("failed to connect to cluster")
	}

	var (
		labelSelector = parseSelector(labelString)
		annotations   = parseSelector(annString)
		namespaces    = parseSelector(nsString)
	)

	log.WithFields(log.Fields{
		"labels":      labelSelector,
		"annotations": annotations,
		"namespaces":  namespaces,
		"minimumAge":  minimumAge,
	}).Info("setting pod filter")

	parsedWeekdays := util.ParseWeekdays(excludedWeekdays)
	parsedTimesOfDay, err := util.ParseTimePeriods(excludedTimesOfDay)
	if err != nil {
		log.WithFields(log.Fields{
			"timesOfDay": excludedTimesOfDay,
			"err":        err,
		}).Fatal("failed to parse times of day")
	}
	parsedDaysOfYear, err := util.ParseDays(excludedDaysOfYear)
	if err != nil {
		log.WithFields(log.Fields{
			"daysOfYear": excludedDaysOfYear,
			"err":        err,
		}).Fatal("failed to parse days of year")
	}

	log.WithFields(log.Fields{
		"weekdays":   parsedWeekdays,
		"timesOfDay": parsedTimesOfDay,
		"daysOfYear": formatDays(parsedDaysOfYear),
	}).Info("setting quiet times")

	parsedTimezone, err := time.LoadLocation(timezone)
	if err != nil {
		log.WithFields(log.Fields{
			"timeZone": timezone,
			"err":      err,
		}).Fatal("failed to detect time zone")
	}
	timezoneName, offset := time.Now().In(parsedTimezone).Zone()

	log.WithFields(log.Fields{
		"name":     timezoneName,
		"location": parsedTimezone,
		"offset":   offset / int(time.Hour/time.Second),
	}).Info("setting timezone")

	chaoskube := chaoskube.New(
		client,
		labelSelector,
		annotations,
		namespaces,
		parsedWeekdays,
		parsedTimesOfDay,
		parsedDaysOfYear,
		parsedTimezone,
		minimumAge,
		log.StandardLogger(),
		dryRun,
		terminator.NewDeletePodTerminator(client, log.StandardLogger(), gracePeriod),
	)

	if metricsAddress != "" {
		http.Handle("/metrics", promhttp.Handler())
		http.HandleFunc("/healthz",
			func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintln(w, "OK")
			})
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(`
					<title>chaoskube</title>

chaoskube

Metrics

Health Check

					`))
		})
		go func() {
			if err := http.ListenAndServe(metricsAddress, nil); err != nil {
				log.WithFields(log.Fields{
					"err": err,
				}).Fatal("failed to start HTTP server")
			}
		}()
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		<-done
		cancel()
	}()

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	chaoskube.Run(ctx, ticker.C)
}

func newClient() (*kubernetes.Clientset, error) {
	if kubeconfig == "" {
		if _, err := os.Stat(clientcmd.RecommendedHomeFile); err == nil {
			kubeconfig = clientcmd.RecommendedHomeFile
		}
	}

	log.WithFields(log.Fields{
		"kubeconfig": kubeconfig,
		"master":     master,
	}).Debug("using cluster config")

	config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
	if err != nil {
		return nil, err
	}

	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	serverVersion, err := client.Discovery().ServerVersion()
	if err != nil {
		return nil, err
	}

	log.WithFields(log.Fields{
		"master":        config.Host,
		"serverVersion": serverVersion,
	}).Info("connected to cluster")

	return client, nil
}

func parseSelector(str string) labels.Selector {
	selector, err := labels.Parse(str)
	if err != nil {
		log.WithFields(log.Fields{
			"selector": str,
			"err":      err,
		}).Fatal("failed to parse selector")
	}
	return selector
}

func formatDays(days []time.Time) []string {
	formattedDays := make([]string, 0, len(days))
	for _, d := range days {
		formattedDays = append(formattedDays, d.Format(util.YearDay))
	}
	return formattedDays
}
<commit_msg>chore: debug print logFormat flag on start<commit_after>package main

import (
	"context"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"

	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/linki/chaoskube/chaoskube"
	"github.com/linki/chaoskube/terminator"
	"github.com/linki/chaoskube/util"
)

var (
	version = "undefined"
)

var (
	labelString        string
	annString          string
	nsString           string
	excludedWeekdays   string
	excludedTimesOfDay string
	excludedDaysOfYear string
	timezone           string
	minimumAge         time.Duration
	master             string
	kubeconfig         string
	interval           time.Duration
	dryRun             bool
	debug              bool
	metricsAddress     string
	gracePeriod        time.Duration
	logFormat          string
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())

	kingpin.Flag("labels", "A set of labels to restrict the list of affected pods. Defaults to everything.").StringVar(&labelString)
	kingpin.Flag("annotations", "A set of annotations to restrict the list of affected pods. Defaults to everything.").StringVar(&annString)
	kingpin.Flag("namespaces", "A set of namespaces to restrict the list of affected pods. Defaults to everything.").StringVar(&nsString)
	kingpin.Flag("excluded-weekdays", "A list of weekdays when termination is suspended, e.g. Sat,Sun").StringVar(&excludedWeekdays)
	kingpin.Flag("excluded-times-of-day", "A list of time periods of a day when termination is suspended, e.g. 22:00-08:00").StringVar(&excludedTimesOfDay)
	kingpin.Flag("excluded-days-of-year", "A list of days of a year when termination is suspended, e.g. Apr1,Dec24").StringVar(&excludedDaysOfYear)
	kingpin.Flag("timezone", "The timezone by which to interpret the excluded weekdays and times of day, e.g. UTC, Local, Europe/Berlin. Defaults to UTC.").Default("UTC").StringVar(&timezone)
	kingpin.Flag("minimum-age", "Minimum age of pods to consider for termination").Default("0s").DurationVar(&minimumAge)
	kingpin.Flag("master", "The address of the Kubernetes cluster to target").StringVar(&master)
	kingpin.Flag("kubeconfig", "Path to a kubeconfig file").StringVar(&kubeconfig)
	kingpin.Flag("interval", "Interval between Pod terminations").Default("10m").DurationVar(&interval)
	kingpin.Flag("dry-run", "If true, don't actually do anything.").Default("true").BoolVar(&dryRun)
	kingpin.Flag("debug", "Enable debug logging.").BoolVar(&debug)
	kingpin.Flag("metrics-address", "Listening address for metrics handler").Default(":8080").StringVar(&metricsAddress)
	kingpin.Flag("grace-period", "Grace period to terminate Pods. Negative values will use the Pod's grace period.").Default("-1s").DurationVar(&gracePeriod)
	kingpin.Flag("log-format", "Select log format options").Default("text").EnumVar(&logFormat, "text", "json")
}

func main() {
	kingpin.Version(version)
	kingpin.Parse()

	if debug {
		log.SetLevel(log.DebugLevel)
	}

	if logFormat == "json" {
		log.SetFormatter(&log.JSONFormatter{})
	}

	log.WithFields(log.Fields{
		"labels":             labelString,
		"annotations":        annString,
		"namespaces":         nsString,
		"excludedWeekdays":   excludedWeekdays,
		"excludedTimesOfDay": excludedTimesOfDay,
		"excludedDaysOfYear": excludedDaysOfYear,
		"timezone":           timezone,
		"minimumAge":         minimumAge,
		"master":             master,
		"kubeconfig":         kubeconfig,
		"interval":           interval,
		"dryRun":             dryRun,
		"debug":              debug,
		"metricsAddress":     metricsAddress,
		"gracePeriod":        gracePeriod,
		"logFormat":          logFormat,
	}).Debug("reading config")

	log.WithFields(log.Fields{
		"version":  version,
		"dryRun":   dryRun,
		"interval": interval,
	}).Info("starting up")

	client, err := newClient()
	if err != nil {
		log.WithField("err", err).Fatal("failed to connect to cluster")
	}

	var (
		labelSelector = parseSelector(labelString)
		annotations   = parseSelector(annString)
		namespaces    = parseSelector(nsString)
	)

	log.WithFields(log.Fields{
		"labels":      labelSelector,
		"annotations": annotations,
		"namespaces":  namespaces,
		"minimumAge":  minimumAge,
	}).Info("setting pod filter")

	parsedWeekdays := util.ParseWeekdays(excludedWeekdays)
	parsedTimesOfDay, err := util.ParseTimePeriods(excludedTimesOfDay)
	if err != nil {
		log.WithFields(log.Fields{
			"timesOfDay": excludedTimesOfDay,
			"err":        err,
		}).Fatal("failed to parse times of day")
	}
	parsedDaysOfYear, err := util.ParseDays(excludedDaysOfYear)
	if err != nil {
		log.WithFields(log.Fields{
			"daysOfYear": excludedDaysOfYear,
			"err":        err,
		}).Fatal("failed to parse days of year")
	}

	log.WithFields(log.Fields{
		"weekdays":   parsedWeekdays,
		"timesOfDay": parsedTimesOfDay,
		"daysOfYear": formatDays(parsedDaysOfYear),
	}).Info("setting quiet times")

	parsedTimezone, err := time.LoadLocation(timezone)
	if err != nil {
		log.WithFields(log.Fields{
			"timeZone": timezone,
			"err":      err,
		}).Fatal("failed to detect time zone")
	}
	timezoneName, offset := time.Now().In(parsedTimezone).Zone()

	log.WithFields(log.Fields{
		"name":     timezoneName,
		"location": parsedTimezone,
		"offset":   offset / int(time.Hour/time.Second),
	}).Info("setting timezone")

	chaoskube := chaoskube.New(
		client,
		labelSelector,
		annotations,
		namespaces,
		parsedWeekdays,
		parsedTimesOfDay,
		parsedDaysOfYear,
		parsedTimezone,
		minimumAge,
		log.StandardLogger(),
		dryRun,
		terminator.NewDeletePodTerminator(client, log.StandardLogger(), gracePeriod),
	)

	if metricsAddress != "" {
		http.Handle("/metrics", promhttp.Handler())
		http.HandleFunc("/healthz",
			func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintln(w, "OK")
			})
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(`
					<title>chaoskube</title>

chaoskube

Metrics

Health Check

					`))
		})
		go func() {
			if err := http.ListenAndServe(metricsAddress, nil); err != nil {
				log.WithFields(log.Fields{
					"err": err,
				}).Fatal("failed to start HTTP server")
			}
		}()
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		<-done
		cancel()
	}()

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	chaoskube.Run(ctx, ticker.C)
}

func newClient() (*kubernetes.Clientset, error) {
	if kubeconfig == "" {
		if _, err := os.Stat(clientcmd.RecommendedHomeFile); err == nil {
			kubeconfig = clientcmd.RecommendedHomeFile
		}
	}

	log.WithFields(log.Fields{
		"kubeconfig": kubeconfig,
		"master":     master,
	}).Debug("using cluster config")

	config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
	if err != nil {
		return nil, err
	}

	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	serverVersion, err := client.Discovery().ServerVersion()
	if err != nil {
		return nil, err
	}

	log.WithFields(log.Fields{
		"master":        config.Host,
		"serverVersion": serverVersion,
	}).Info("connected to cluster")

	return client, nil
}

func parseSelector(str string) labels.Selector {
	selector, err := labels.Parse(str)
	if err != nil {
		log.WithFields(log.Fields{
			"selector": str,
			"err":      err,
		}).Fatal("failed to parse selector")
	}
	return selector
}

func formatDays(days []time.Time) []string {
	formattedDays := make([]string, 0, len(days))
	for _, d := range days {
		formattedDays = append(formattedDays, d.Format(util.YearDay))
	}
	return formattedDays
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/BurntSushi/toml"
	"github.com/k0pernicus/goyave/configurationFile"
	"github.com/k0pernicus/goyave/consts"
	"github.com/k0pernicus/goyave/traces"
	"github.com/k0pernicus/goyave/utils"
	"github.com/k0pernicus/goyave/walk"
	"github.com/spf13/cobra"
)

var configurationFileStructure configurationFile.ConfigurationFile
var configurationFilePath string
var userHomeDir string

/*initialize get the configuration file existing in the system (or create it), and return
 *a pointer to his content.
 */
func initialize(configurationFileStructure *configurationFile.ConfigurationFile) {
	// Initialize all different traces structures
	traces.InitTraces(os.Stdout, os.Stderr, os.Stdout, os.Stdout)

	// Get the user home directory
	userHomeDir = utils.GetUserHomeDir()
	if len(userHomeDir) == 0 {
		log.Fatalf("Cannot get the user home dir.\n")
	}
	// Set the configuration path file
	configurationFilePath = path.Join(userHomeDir, consts.ConfigurationFileName)
	filePointer, err := os.OpenFile(configurationFilePath, os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		log.Fatalf("Cannot open the file %s, due to error '%s'.\n", configurationFilePath, err)
	}
	defer filePointer.Close()
	var bytesArray []byte
	// Get the content of the goyave configuration file
	configurationFile.GetConfigurationFileContent(filePointer, &bytesArray)
	if _, err = toml.Decode(string(bytesArray[:]), configurationFileStructure); err != nil {
		log.Fatalln(err)
	}
}

/*kill saves the current state of the configuration structure in the configuration file
 */
func kill() {
	var outputBuffer bytes.Buffer
	if err := configurationFileStructure.Encode(&outputBuffer); err != nil {
		log.Fatalln("Cannot save the current configurationFile structure!")
	}
	if err := ioutil.WriteFile(configurationFilePath, outputBuffer.Bytes(), 0777); err != nil {
		log.Fatalln("Cannot access to your file to save the configurationFile structure!")
	}
}

func main() {

	/*rootCmd defines the global app, and some actions to run before and after the command running
	 */
	var rootCmd = &cobra.Command{
		Use:   "goyave",
		Short: "Goyave is a tool to take a look at your local git repositories",
		// Initialize the structure
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			initialize(&configurationFileStructure)
		},
		// Save the current configuration file structure, in the configuration file
		PersistentPostRun: func(cmd *cobra.Command, args []string) {
			kill()
		},
	}

	/*crawlCmd is a subcommand to crawl your hard drive in order to get and save new git repositories
	 */
	var crawlCmd = &cobra.Command{
		Use:   "crawl",
		Short: "Crawl the hard drive in order to find git repositories",
		Run: func(cmd *cobra.Command, args []string) {
			//		Get all git paths, and display them
			gitPaths, err := walk.RetrieveGitRepositories(userHomeDir)
			if err != nil {
				log.Fatalf("There was an error retrieving your git repositories: '%s'\n", err)
			}
			// For each git repository, check if it exists, and if not add it to the default target visibility
			for _, gitPath := range gitPaths {
				if err := configurationFileStructure.AddRepository(gitPath, configurationFileStructure.Local.DefaultTarget); err != nil {
					traces.WarningTracer.Printf("[%s] %s", gitPath, err)
				}
			}
			//For each VISIBLE repository, get some informations about his state and display it
		},
	}

	/*stateCmd is a subcommand to list the state of each local git repository
	 */
	var stateCmd = &cobra.Command{
		Use:   "state",
		Short: "Get the state of each local git repository",
		Run: func(cmd *cobra.Command, args []string) {
			for _, gitStruct := range configurationFileStructure.VisibleRepositories {
				gitStruct.Init()
				gitStruct.GitObject.Status()
			}
		},
	}

	rootCmd.AddCommand(crawlCmd, stateCmd)

	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<commit_msg>Add 'add' command<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/BurntSushi/toml"
	"github.com/k0pernicus/goyave/configurationFile"
	"github.com/k0pernicus/goyave/consts"
	"github.com/k0pernicus/goyave/traces"
	"github.com/k0pernicus/goyave/utils"
	"github.com/k0pernicus/goyave/walk"
	"github.com/spf13/cobra"
)

var configurationFileStructure configurationFile.ConfigurationFile
var configurationFilePath string
var userHomeDir string

/*initialize get the configuration file existing in the system (or create it), and return
 *a pointer to his content.
 */
func initialize(configurationFileStructure *configurationFile.ConfigurationFile) {
	// Initialize all different traces structures
	traces.InitTraces(os.Stdout, os.Stderr, os.Stdout, os.Stdout)

	// Get the user home directory
	userHomeDir = utils.GetUserHomeDir()
	if len(userHomeDir) == 0 {
		log.Fatalf("Cannot get the user home dir.\n")
	}
	// Set the configuration path file
	configurationFilePath = path.Join(userHomeDir, consts.ConfigurationFileName)
	filePointer, err := os.OpenFile(configurationFilePath, os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		log.Fatalf("Cannot open the file %s, due to error '%s'.\n", configurationFilePath, err)
	}
	defer filePointer.Close()
	var bytesArray []byte
	// Get the content of the goyave configuration file
	configurationFile.GetConfigurationFileContent(filePointer, &bytesArray)
	if _, err = toml.Decode(string(bytesArray[:]), configurationFileStructure); err != nil {
		log.Fatalln(err)
	}
}

/*kill saves the current state of the configuration structure in the configuration file
 */
func kill() {
	var outputBuffer bytes.Buffer
	if err := configurationFileStructure.Encode(&outputBuffer); err != nil {
		log.Fatalln("Cannot save the current configurationFile structure!")
	}
	if err := ioutil.WriteFile(configurationFilePath, outputBuffer.Bytes(), 0777); err != nil {
		log.Fatalln("Cannot access to your file to save the configurationFile structure!")
	}
}

func main() {

	/*rootCmd defines the global app, and some actions to run before and after the command running
	 */
	var rootCmd = &cobra.Command{
		Use:   "goyave",
		Short: "Goyave is a tool to take a look at your local git repositories",
		// Initialize the structure
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			initialize(&configurationFileStructure)
		},
		// Save the current configuration file structure, in the configuration file
		PersistentPostRun: func(cmd *cobra.Command, args []string) {
			kill()
		},
	}

	var addCmd = &cobra.Command{
		Use:   "add",
		Short: "Add the current path as a VISIBLE repository",
		Run: func(cmd *cobra.Command, args []string) {
			// Get the path where the command has been executed
			currentDir, err := os.Getwd()
			if err != nil {
				log.Fatalln("There was a problem retrieving the current directory")
			}
			if !utils.IsGitRepository(currentDir) {
				log.Fatalf("%s is not a git repository!\n", currentDir)
			}
			if err := configurationFileStructure.AddRepository(currentDir, consts.VisibleFlag); err != nil {
				traces.WarningTracer.Printf("[%s] %s\n", currentDir, err)
			}
		},
	}

	/*crawlCmd is a subcommand to crawl your hard drive in order to get and save new git repositories
	 */
	var crawlCmd = &cobra.Command{
		Use:   "crawl",
		Short: "Crawl the hard drive in order to find git repositories",
		Run: func(cmd *cobra.Command, args []string) {
			//		Get all git paths, and display them
			gitPaths, err := walk.RetrieveGitRepositories(userHomeDir)
			if err != nil {
				log.Fatalf("There was an error retrieving your git repositories: '%s'\n", err)
			}
			// For each git repository, check if it exists, and if not add it to the default target visibility
			for _, gitPath := range gitPaths {
				if err := configurationFileStructure.AddRepository(gitPath, configurationFileStructure.Local.DefaultTarget); err != nil {
					traces.WarningTracer.Printf("[%s] %s", gitPath, err)
				}
			}
			//For each VISIBLE repository, get some informations about his state and display it
		},
	}

	/*stateCmd is a subcommand to list the state of each local git repository
	 */
	var stateCmd = &cobra.Command{
		Use:   "state",
		Short: "Get the state of each local git repository",
		Run: func(cmd *cobra.Command, args []string) {
			for _, gitStruct := range configurationFileStructure.VisibleRepositories {
				gitStruct.Init()
				gitStruct.GitObject.Status()
			}
		},
	}

	rootCmd.AddCommand(addCmd, crawlCmd, stateCmd)

	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/nranchev/go-libGeoIP"
	"github.com/packetbeat/gopacket/layers"
	"github.com/packetbeat/gopacket/pcap"
)

const Version = "0.4.1"

// Structure grouping main components/modules
type PacketbeatStruct struct {
	Sniffer *SnifferSetup
	Decoder *DecoderStruct
}

// Global variable containing the main values
var Packetbeat PacketbeatStruct

type protocolType uint16

const (
	UnknownProtocol protocolType = iota
	HttpProtocol
	MysqlProtocol
	RedisProtocol
	PgsqlProtocol
	ThriftProtocol
)

var protocolNames = []string{"unknown", "http", "mysql", "redis", "pgsql", "thrift"}

type tomlConfig struct {
	Interfaces tomlInterfaces
	RunOptions tomlRunOptions
	Protocols  map[string]tomlProtocol
	Procs      tomlProcs
	Output     map[string]tomlMothership
	Agent      tomlAgent
	Logging    tomlLogging
	Passwords  tomlPasswords
	ContentTypes tomlContentTypes
	Thrift     tomlThrift
}

type tomlRunOptions struct {
	Uid int
	Gid int
}

type tomlLogging struct {
	Selectors []string
}

type tomlPasswords struct {
	Hide_keywords []string
}
type tomlContentTypes struct {
	include_body []string
}

var _Config tomlConfig
var _ConfigMeta toml.MetaData
var _GeoLite *libgeo.GeoIP

func Bytes_Ipv4_Ntoa(bytes []byte) string {
	var strarr []string = make([]string, 4)
	for i, b := range bytes {
		strarr[i] = strconv.Itoa(int(b))
	}
	return strings.Join(strarr, ".")
}

func writeHeapProfile(filename string) {
	f, err := os.Create(filename)
	if err != nil {
		ERR("Failed creating file %s: %s", filename, err)
		return
	}
	pprof.WriteHeapProfile(f)
	f.Close()

	INFO("Created memory profile file %s.", filename)
}

func debugMemStats() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	DEBUG("mem", "Memory stats: In use: %d Total (even if freed): %d System: %d",
		m.Alloc, m.TotalAlloc, m.Sys)
}

func loadGeoIPData() {
	geoip_path := "/usr/share/GeoIP/GeoIP.dat"
	fi, err := os.Lstat(geoip_path)
	if err != nil {
		WARN("Could not load GeoIP data: %s", err.Error())
		return
	}

	if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
		// follow symlink
		geoip_path, err = filepath.EvalSymlinks(geoip_path)
		if err != nil {
			WARN("Could not load GeoIP data: %s", err.Error())
			return
		}
	}

	_GeoLite, err = libgeo.Load(geoip_path)
	if err != nil {
		WARN("Could not load GeoIP data: %s", err.Error())
	}
}

func main() {

	// Use our own FlagSet, because some libraries pollute the global one
	var cmdLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)

	configfile := cmdLine.String("c", "packetbeat.conf", "Configuration file")
	file := cmdLine.String("I", "", "file")
	loop := cmdLine.Int("l", 1, "Loop file. 0 - loop forever")
	debugSelectorsStr := cmdLine.String("d", "", "Enable certain debug selectors")
	oneAtAtime := cmdLine.Bool("O", false, "Read packets one at a time (press Enter)")
	toStdout := cmdLine.Bool("e", false, "Output to stdout instead of syslog")
	topSpeed := cmdLine.Bool("t", false, "Read packets as fast as possible, without sleeping")
	publishDisabled := cmdLine.Bool("N", false, "Disable actual publishing for testing")
	verbose := cmdLine.Bool("v", false, "Log at INFO level")
	printVersion := cmdLine.Bool("version", false, "Print version and exit")
	memprofile := cmdLine.String("memprofile", "", "Write memory profile to this file")
	cpuprofile := cmdLine.String("cpuprofile", "", "Write cpu profile to file")
	dumpfile := cmdLine.String("dump", "", "Write all captured packets to this libpcap file.")

	cmdLine.Parse(os.Args[1:])

	if *printVersion {
		fmt.Printf("Packetbeat version %s (%s)\n", Version, runtime.GOARCH)
		return
	}

	logLevel := LOG_ERR
	if *verbose {
		logLevel = LOG_INFO
	}

	debugSelectors := []string{}
	if len(*debugSelectorsStr) > 0 {
		debugSelectors = strings.Split(*debugSelectorsStr, ",")
		logLevel = LOG_DEBUG
	}

	var err error

	if _ConfigMeta, err = toml.DecodeFile(*configfile, &_Config); err != nil {
		fmt.Printf("TOML config parsing failed on %s: %s. Exiting.\n", *configfile, err)
		return
	}

	if len(debugSelectors) == 0 {
		debugSelectors = _Config.Logging.Selectors
	}
	LogInit(logLevel, "", !*toStdout, debugSelectors)

	if !IS_DEBUG("stdlog") {
		// disable standard logging by default
		log.SetOutput(ioutil.Discard)
	}

	_Config.Interfaces.Bpf_filter = configToFilter(&_Config)
	Packetbeat.Sniffer, err = CreateSniffer(&_Config.Interfaces, file)
	if err != nil {
		CRIT("Error creating sniffer: %s", err)
		return
	}
	sniffer := Packetbeat.Sniffer
	Packetbeat.Decoder, err = CreateDecoder(sniffer.Datalink())
	if err != nil {
		CRIT("Error creating decoder: %s", err)
		return
	}

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

	if err = Publisher.Init(*publishDisabled); err != nil {
		CRIT(err.Error())
		return
	}

	if err = procWatcher.Init(&_Config.Procs); err != nil {
		CRIT(err.Error())
		return
	}

	if err = ThriftMod.Init(false); err != nil {
		CRIT(err.Error())
		return
	}

	if err = HttpMod.Init(false); err != nil {
		CRIT(err.Error())
		return
	}

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

	loadGeoIPData()

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	var dumper *pcap.Dumper = nil
	if *dumpfile != "" {
		p, err := pcap.OpenDead(layers.LinkTypeEthernet, 65535)
		if err != nil {
			CRIT(err.Error())
			return
		}
		dumper, err = p.NewDumper(*dumpfile)
		if err != nil {
			CRIT(err.Error())
			return
		}
	}

	live := true

	// On ^C or SIGTERM, gracefully set live to false
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-sigc
		live = false
		DEBUG("signal", "Received term singal, set live to false")
	}()

	counter := 0
	loopCount := 1
	var lastPktTime *time.Time = nil
	for live {
		if *oneAtAtime {
			fmt.Println("Press enter to read packet")
			fmt.Scanln()
		}

		data, ci, err := sniffer.DataSource.ReadPacketData()

		if err == pcap.NextErrorTimeoutExpired || err == syscall.EINTR {
			DEBUG("pcapread", "Interrupted")
			continue
		}

		if err == io.EOF {
			DEBUG("pcapread", "End of file")
			loopCount += 1
			if *loop > 0 && loopCount > *loop {
				// give a bit of time to the publish goroutine
				// to flush
				time.Sleep(300 * time.Millisecond)
				live = false
				continue
			}

			DEBUG("pcapread", "Reopening the file")
			err = sniffer.Reopen()
			if err != nil {
				CRIT("Error reopening file: %s", err)
				live = false
				continue
			}
			lastPktTime = nil
			continue
		}

		if err != nil {
			CRIT("Sniffing error: %s", err)
			live = false
			continue
		}

		if len(data) == 0 {
			// Empty packet, probably timeout from afpacket
			continue
		}

		if *file != "" {
			if lastPktTime != nil && !*topSpeed {
				sleep := ci.Timestamp.Sub(*lastPktTime)
				if sleep > 0 {
					time.Sleep(sleep)
				} else {
					WARN("Time in pcap went backwards: %d", sleep)
				}
			}
			_lastPktTime := ci.Timestamp
			lastPktTime = &_lastPktTime
			ci.Timestamp = time.Now() // overwrite what we get from the pcap
		}
		counter++

		if dumper != nil {
			dumper.WritePacketData(data, ci)
		}
		DEBUG("pcapread", "Packet number: %d", counter)
		Packetbeat.Decoder.DecodePacketData(data, &ci)
	}
	INFO("Input finish. Processed %d packets. Have a nice day!", counter)

	if *memprofile != "" {
		// wait for all TCP streams to expire
		time.Sleep(TCP_STREAM_EXPIRY * 1.2)
		PrintTcpMap()
		runtime.GC()

		writeHeapProfile(*memprofile)

		debugMemStats()
	}

	if dumper != nil {
		dumper.Close()
	}
}
<commit_msg>Cleaned up tomlContentType<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/nranchev/go-libGeoIP"
	"github.com/packetbeat/gopacket/layers"
	"github.com/packetbeat/gopacket/pcap"
)

const Version = "0.4.1"

// Structure grouping main components/modules
type PacketbeatStruct struct {
	Sniffer *SnifferSetup
	Decoder *DecoderStruct
}

// Global variable containing the main values
var Packetbeat PacketbeatStruct

type protocolType uint16

const (
	UnknownProtocol protocolType = iota
	HttpProtocol
	MysqlProtocol
	RedisProtocol
	PgsqlProtocol
	ThriftProtocol
)

var protocolNames = []string{"unknown", "http", "mysql", "redis", "pgsql", "thrift"}

type tomlConfig struct {
	Interfaces tomlInterfaces
	RunOptions tomlRunOptions
	Protocols  map[string]tomlProtocol
	Procs      tomlProcs
	Output     map[string]tomlMothership
	Agent      tomlAgent
	Logging    tomlLogging
	Passwords  tomlPasswords
	ContentTypes tomlContentTypes
	Thrift     tomlThrift
}

type tomlRunOptions struct {
	Uid int
	Gid int
}

type tomlLogging struct {
	Selectors []string
}

type tomlPasswords struct {
	Hide_keywords []string
}
type tomlContentTypes struct {
	Include_body []string
}

var _Config tomlConfig
var _ConfigMeta toml.MetaData
var _GeoLite *libgeo.GeoIP

func Bytes_Ipv4_Ntoa(bytes []byte) string {
	var strarr []string = make([]string, 4)
	for i, b := range bytes {
		strarr[i] = strconv.Itoa(int(b))
	}
	return strings.Join(strarr, ".")
}

func writeHeapProfile(filename string) {
	f, err := os.Create(filename)
	if err != nil {
		ERR("Failed creating file %s: %s", filename, err)
		return
	}
	pprof.WriteHeapProfile(f)
	f.Close()

	INFO("Created memory profile file %s.", filename)
}

func debugMemStats() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	DEBUG("mem", "Memory stats: In use: %d Total (even if freed): %d System: %d",
		m.Alloc, m.TotalAlloc, m.Sys)
}

func loadGeoIPData() {
	geoip_path := "/usr/share/GeoIP/GeoIP.dat"
	fi, err := os.Lstat(geoip_path)
	if err != nil {
		WARN("Could not load GeoIP data: %s", err.Error())
		return
	}

	if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
		// follow symlink
		geoip_path, err = filepath.EvalSymlinks(geoip_path)
		if err != nil {
			WARN("Could not load GeoIP data: %s", err.Error())
			return
		}
	}

	_GeoLite, err = libgeo.Load(geoip_path)
	if err != nil {
		WARN("Could not load GeoIP data: %s", err.Error())
	}
}

func main() {

	// Use our own FlagSet, because some libraries pollute the global one
	var cmdLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)

	configfile := cmdLine.String("c", "packetbeat.conf", "Configuration file")
	file := cmdLine.String("I", "", "file")
	loop := cmdLine.Int("l", 1, "Loop file. 0 - loop forever")
	debugSelectorsStr := cmdLine.String("d", "", "Enable certain debug selectors")
	oneAtAtime := cmdLine.Bool("O", false, "Read packets one at a time (press Enter)")
	toStdout := cmdLine.Bool("e", false, "Output to stdout instead of syslog")
	topSpeed := cmdLine.Bool("t", false, "Read packets as fast as possible, without sleeping")
	publishDisabled := cmdLine.Bool("N", false, "Disable actual publishing for testing")
	verbose := cmdLine.Bool("v", false, "Log at INFO level")
	printVersion := cmdLine.Bool("version", false, "Print version and exit")
	memprofile := cmdLine.String("memprofile", "", "Write memory profile to this file")
	cpuprofile := cmdLine.String("cpuprofile", "", "Write cpu profile to file")
	dumpfile := cmdLine.String("dump", "", "Write all captured packets to this libpcap file.")

	cmdLine.Parse(os.Args[1:])

	if *printVersion {
		fmt.Printf("Packetbeat version %s (%s)\n", Version, runtime.GOARCH)
		return
	}

	logLevel := LOG_ERR
	if *verbose {
		logLevel = LOG_INFO
	}

	debugSelectors := []string{}
	if len(*debugSelectorsStr) > 0 {
		debugSelectors = strings.Split(*debugSelectorsStr, ",")
		logLevel = LOG_DEBUG
	}

	var err error

	if _ConfigMeta, err = toml.DecodeFile(*configfile, &_Config); err != nil {
		fmt.Printf("TOML config parsing failed on %s: %s. Exiting.\n", *configfile, err)
		return
	}
	if len(debugSelectors) == 0 {
		debugSelectors = _Config.Logging.Selectors
	}
	LogInit(logLevel, "", !*toStdout, debugSelectors)

	if !IS_DEBUG("stdlog") {
		// disable standard logging by default
		log.SetOutput(ioutil.Discard)
	}

	_Config.Interfaces.Bpf_filter = configToFilter(&_Config)
	Packetbeat.Sniffer, err = CreateSniffer(&_Config.Interfaces, file)
	if err != nil {
		CRIT("Error creating sniffer: %s", err)
		return
	}
	sniffer := Packetbeat.Sniffer
	Packetbeat.Decoder, err = CreateDecoder(sniffer.Datalink())
	if err != nil {
		CRIT("Error creating decoder: %s", err)
		return
	}

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

	if err = Publisher.Init(*publishDisabled); err != nil {
		CRIT(err.Error())
		return
	}

	if err = procWatcher.Init(&_Config.Procs); err != nil {
		CRIT(err.Error())
		return
	}

	if err = ThriftMod.Init(false); err != nil {
		CRIT(err.Error())
		return
	}

	if err = HttpMod.Init(false); err != nil {
		CRIT(err.Error())
		return
	}

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

	loadGeoIPData()

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	var dumper *pcap.Dumper = nil
	if *dumpfile != "" {
		p, err := pcap.OpenDead(layers.LinkTypeEthernet, 65535)
		if err != nil {
			CRIT(err.Error())
			return
		}
		dumper, err = p.NewDumper(*dumpfile)
		if err != nil {
			CRIT(err.Error())
			return
		}
	}

	live := true

	// On ^C or SIGTERM, gracefully set live to false
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-sigc
		live = false
		DEBUG("signal", "Received term singal, set live to false")
	}()

	counter := 0
	loopCount := 1
	var lastPktTime *time.Time = nil
	for live {
		if *oneAtAtime {
			fmt.Println("Press enter to read packet")
			fmt.Scanln()
		}

		data, ci, err := sniffer.DataSource.ReadPacketData()

		if err == pcap.NextErrorTimeoutExpired || err == syscall.EINTR {
			DEBUG("pcapread", "Interrupted")
			continue
		}

		if err == io.EOF {
			DEBUG("pcapread", "End of file")
			loopCount += 1
			if *loop > 0 && loopCount > *loop {
				// give a bit of time to the publish goroutine
				// to flush
				time.Sleep(300 * time.Millisecond)
				live = false
				continue
			}

			DEBUG("pcapread", "Reopening the file")
			err = sniffer.Reopen()
			if err != nil {
				CRIT("Error reopening file: %s", err)
				live = false
				continue
			}
			lastPktTime = nil
			continue
		}

		if err != nil {
			CRIT("Sniffing error: %s", err)
			live = false
			continue
		}

		if len(data) == 0 {
			// Empty packet, probably timeout from afpacket
			continue
		}

		if *file != "" {
			if lastPktTime != nil && !*topSpeed {
				sleep := ci.Timestamp.Sub(*lastPktTime)
				if sleep > 0 {
					time.Sleep(sleep)
				} else {
					WARN("Time in pcap went backwards: %d", sleep)
				}
			}
			_lastPktTime := ci.Timestamp
			lastPktTime = &_lastPktTime
			ci.Timestamp = time.Now() // overwrite what we get from the pcap
		}
		counter++

		if dumper != nil {
			dumper.WritePacketData(data, ci)
		}
		DEBUG("pcapread", "Packet number: %d", counter)
		Packetbeat.Decoder.DecodePacketData(data, &ci)
	}
	INFO("Input finish. Processed %d packets. Have a nice day!", counter)

	if *memprofile != "" {
		// wait for all TCP streams to expire
		time.Sleep(TCP_STREAM_EXPIRY * 1.2)
		PrintTcpMap()
		runtime.GC()

		writeHeapProfile(*memprofile)

		debugMemStats()
	}

	if dumper != nil {
		dumper.Close()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"time"

	"github.com/tomohiro/go-gyazo/gyazo"
	"github.com/urfave/cli"
	"github.com/mitchellh/go-homedir"
	"github.com/skratchdot/open-golang/open"
)

var (
	// exitCode to terminate.
	exitCode = 0

	// Default endpoint.
	endpoint = "http://upload.gyazo.com/upload.cgi"
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	app := cli.NewApp()
	app.Name = "gyazo"
	app.Version = Version
	app.Usage = "Gyazo command-line uploader"
	app.Author = "Tomohiro TAIRA"
	app.Email = "tomohiro.t@gmail.com"
	app.Action = upload
	app.Run(os.Args)

	return exitCode
}

// Set the endpoint of self hosting Gyazo server URL.
func init() {
	if os.Getenv("GYAZO_SERVER_URL") != "" {
		endpoint = os.Getenv("GYAZO_SERVER_URL")
	}
}

// Upload a new image to a Gyazo from the specified local image file.
func upload(c *cli.Context) {
	var filename string
	var url string
	var err error

	filename = c.Args().First()

	if filename == "" {
		filename, err = takeScreenshot()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to take a screenshot: %s\n", err)
			exitCode = 1
			return
		}
	}

	if !supportedMimetype(filename) {
		fmt.Fprint(os.Stderr, "Failed to upload: unsupported file type\n")
		exitCode = 1
		return
	}

	// Open and load the content from an image.
	content, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open and read: %s\n", filename)
		return
	}
	defer content.Close()

	if os.Getenv("GYAZO_ACCESS_TOKEN") != "" {
		gyazo, _ := gyazo.NewClient(os.Getenv("GYAZO_ACCESS_TOKEN"))
		image, err := gyazo.Upload(content)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to upload: %s\n", err)
			exitCode = 1
			return
		}
		url = image.PermalinkURL
	} else {
		// Create multipart/form-data
		body := &bytes.Buffer{}
		writer := multipart.NewWriter(body)

		part, err := writer.CreateFormFile("imagedata", filename)
		if err != nil {
			exitCode = 1
			return
		}

		if _, err = io.Copy(part, content); err != nil {
			exitCode = 1
			return
		}

		writer.WriteField("id", gyazoID())
		err = writer.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to create multipart/data: %s\n", err)
			exitCode = 1
			return
		}

		res, err := http.Post(endpoint, writer.FormDataContentType(), body)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to upload: %s\n", err)
			exitCode = 1
			return
		}
		defer res.Body.Close()

		url, err = imageURL(res)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Response error: %s\n", err)
			exitCode = 1
			return
		}
	}

	fmt.Println(url)
	err = open.Run(url)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open by default browser: %s\n", err)
	}
}

// takeScreenshot takes a screenshot and then returns it file path.
func takeScreenshot() (string, error) {
	var err error
	path := fmt.Sprintf("/tmp/image_upload%d.png", os.Getpid())

	switch runtime.GOOS {
	case "darwin":
		err = exec.Command("screencapture", "-i", path).Run()
	case "linux":
		err = exec.Command("import", path).Run()
	case "windows":
		err = errors.New("unsupported os")
	}

	return path, err
}

// supportedMimetype returns result of checked mimetype.
func supportedMimetype(f string) bool {
	t := mime.TypeByExtension(filepath.Ext(f))
	res, _ := regexp.MatchString("image", t)
	return res
}

// imageURL returns url of uploaded image.
func imageURL(r *http.Response) (string, error) {
	var url string

	id := r.Header.Get("X-Gyazo-Id")
	if err := storeGyazoID(id); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to store Gyazo ID: %s\n", err)
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return url, err
	}

	url = string(body)

	return url, err
}

// gyazoID returns Gyazo ID from stored file.
func gyazoID() string {
	var id string
	body, err := ioutil.ReadFile(gyazoIDPath())
	if err != nil {
		return id
	}

	id = string(body)
	return id
}

// storeGyazoID stores Gyazo ID to file.
func storeGyazoID(id string) error {
	var err error
	if id == "" {
		return err
	}

	path := gyazoIDPath()

	dir := filepath.Dir(path)
	_, err = os.Stat(dir)
	if err != nil {
		err = os.Mkdir(dir, 0755)
		if err != nil {
			return err
		}
	}

	_, err = os.Stat(path)
	if err == nil {
		newpath := fmt.Sprintf("%s_%s.bak", id, time.Now().Format("20060102150405"))
		err = os.Rename(path, newpath)
		if err != nil {
			return err
		}
	}

	buf := bytes.NewBufferString(id)
	err = ioutil.WriteFile(path, buf.Bytes(), 0644)
	if err != nil {
		return err
	}
	return err
}

// gyazoIDPath returns path of Gyazo ID file on local filesystem.
func gyazoIDPath() string {
	homedir, _ := homedir.Dir()

	var path string
	switch runtime.GOOS {
	case "darwin":
		path = fmt.Sprintf("%s/Library/Gyazo/id", homedir)
	case "linux":
		path = fmt.Sprintf("%s/.gyazo.id", homedir)
	case "windows":
		path = fmt.Sprintf("%s\\Gyazo\\id.txt", os.Getenv("APPDATA"))
	}

	return path
}
<commit_msg>Update app info<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"time"

	"github.com/mitchellh/go-homedir"
	"github.com/skratchdot/open-golang/open"
	"github.com/tomohiro/go-gyazo/gyazo"
	"github.com/urfave/cli"
)

var (
	// exitCode to terminate.
	exitCode = 0

	// Default endpoint.
	endpoint = "http://upload.gyazo.com/upload.cgi"
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	app := cli.NewApp()
	app.Name = "gyazo-cli"
	app.Version = Version
	app.Usage = "Gyazo command-line uploader"
	app.Author = "Tomohiro Taira"
	app.Email = "tomohiro.t@gmail.com"
	app.Action = upload
	app.Run(os.Args)

	return exitCode
}

// Set the endpoint of self hosting Gyazo server URL.
func init() {
	if os.Getenv("GYAZO_SERVER_URL") != "" {
		endpoint = os.Getenv("GYAZO_SERVER_URL")
	}
}

// Upload a new image to a Gyazo from the specified local image file.
func upload(c *cli.Context) {
	var filename string
	var url string
	var err error

	filename = c.Args().First()

	if filename == "" {
		filename, err = takeScreenshot()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to take a screenshot: %s\n", err)
			exitCode = 1
			return
		}
	}

	if !supportedMimetype(filename) {
		fmt.Fprint(os.Stderr, "Failed to upload: unsupported file type\n")
		exitCode = 1
		return
	}

	// Open and load the content from an image.
	content, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open and read: %s\n", filename)
		return
	}
	defer content.Close()

	if os.Getenv("GYAZO_ACCESS_TOKEN") != "" {
		gyazo, _ := gyazo.NewClient(os.Getenv("GYAZO_ACCESS_TOKEN"))
		image, err := gyazo.Upload(content)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to upload: %s\n", err)
			exitCode = 1
			return
		}
		url = image.PermalinkURL
	} else {
		// Create multipart/form-data
		body := &bytes.Buffer{}
		writer := multipart.NewWriter(body)

		part, err := writer.CreateFormFile("imagedata", filename)
		if err != nil {
			exitCode = 1
			return
		}

		if _, err = io.Copy(part, content); err != nil {
			exitCode = 1
			return
		}

		writer.WriteField("id", gyazoID())
		err = writer.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to create multipart/data: %s\n", err)
			exitCode = 1
			return
		}

		res, err := http.Post(endpoint, writer.FormDataContentType(), body)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to upload: %s\n", err)
			exitCode = 1
			return
		}
		defer res.Body.Close()

		url, err = imageURL(res)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Response error: %s\n", err)
			exitCode = 1
			return
		}
	}

	fmt.Println(url)
	err = open.Run(url)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open by default browser: %s\n", err)
	}
}

// takeScreenshot takes a screenshot and then returns it file path.
func takeScreenshot() (string, error) {
	var err error
	path := fmt.Sprintf("/tmp/image_upload%d.png", os.Getpid())

	switch runtime.GOOS {
	case "darwin":
		err = exec.Command("screencapture", "-i", path).Run()
	case "linux":
		err = exec.Command("import", path).Run()
	case "windows":
		err = errors.New("unsupported os")
	}

	return path, err
}

// supportedMimetype returns result of checked mimetype.
func supportedMimetype(f string) bool {
	t := mime.TypeByExtension(filepath.Ext(f))
	res, _ := regexp.MatchString("image", t)
	return res
}

// imageURL returns url of uploaded image.
func imageURL(r *http.Response) (string, error) {
	var url string

	id := r.Header.Get("X-Gyazo-Id")
	if err := storeGyazoID(id); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to store Gyazo ID: %s\n", err)
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return url, err
	}

	url = string(body)

	return url, err
}

// gyazoID returns Gyazo ID from stored file.
func gyazoID() string {
	var id string
	body, err := ioutil.ReadFile(gyazoIDPath())
	if err != nil {
		return id
	}

	id = string(body)
	return id
}

// storeGyazoID stores Gyazo ID to file.
func storeGyazoID(id string) error {
	var err error
	if id == "" {
		return err
	}

	path := gyazoIDPath()

	dir := filepath.Dir(path)
	_, err = os.Stat(dir)
	if err != nil {
		err = os.Mkdir(dir, 0755)
		if err != nil {
			return err
		}
	}

	_, err = os.Stat(path)
	if err == nil {
		newpath := fmt.Sprintf("%s_%s.bak", id, time.Now().Format("20060102150405"))
		err = os.Rename(path, newpath)
		if err != nil {
			return err
		}
	}

	buf := bytes.NewBufferString(id)
	err = ioutil.WriteFile(path, buf.Bytes(), 0644)
	if err != nil {
		return err
	}
	return err
}

// gyazoIDPath returns path of Gyazo ID file on local filesystem.
func gyazoIDPath() string {
	homedir, _ := homedir.Dir()

	var path string
	switch runtime.GOOS {
	case "darwin":
		path = fmt.Sprintf("%s/Library/Gyazo/id", homedir)
	case "linux":
		path = fmt.Sprintf("%s/.gyazo.id", homedir)
	case "windows":
		path = fmt.Sprintf("%s\\Gyazo\\id.txt", os.Getenv("APPDATA"))
	}

	return path
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"sync"
	"time"
)

var (
	path  = flag.String("config", "", "Path to config in form of file or URL, required")
	debug = flag.Bool("debug", false, "Print debug info, optional")
)

func main() {
	flag.Parse()
	log.SetFlags(log.Lshortfile | log.LstdFlags)
	rand.Seed(time.Now().UnixNano())

	if *path == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	conf, err := getConfig(*path)
	if err != nil {
		log.Fatal("Can't process config: ", err)
	}

	var wg sync.WaitGroup
	if len(conf.Cloudflare) > 0 {
		fmt.Println("Starting Cloudfront task ...")
		wg.Add(len(conf.Cloudflare))
		invalidateCloudflare(conf.Cloudflare, &wg)
	}
	if len(conf.Cloudfront) > 0 {
		fmt.Println("Starting Cloudflare task ...")
		wg.Add(len(conf.Cloudfront))
		invalidateCloudfront(conf.Cloudfront, &wg)
	}
	wg.Wait()
	fmt.Println("Done!")
}
<commit_msg>Delete some console output<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"sync"
	"time"
)

var (
	path  = flag.String("config", "", "Path to config in form of file or URL, required")
	debug = flag.Bool("debug", false, "Print debug info, optional")
)

func main() {
	flag.Parse()
	log.SetFlags(log.Lshortfile | log.LstdFlags)
	rand.Seed(time.Now().UnixNano())

	if *path == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	conf, err := getConfig(*path)
	if err != nil {
		log.Fatal("Can't process config: ", err)
	}

	var wg sync.WaitGroup
	if len(conf.Cloudflare) > 0 {
		wg.Add(len(conf.Cloudflare))
		invalidateCloudflare(conf.Cloudflare, &wg)
	}
	if len(conf.Cloudfront) > 0 {
		wg.Add(len(conf.Cloudfront))
		invalidateCloudfront(conf.Cloudfront, &wg)
	}
	wg.Wait()
	fmt.Println("Done!")
}
<|endoftext|>


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

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/garyburd/redigo/redis"
	db "github.com/lancetw/hcfd-forecast/helper"
	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	strID := os.Getenv("ChannelID")
	numID, err := strconv.ParseInt(strID, 10, 64)
	if err != nil {
		log.Fatal("Wrong environment setting about ChannelID")
	}

	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()
		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {
			user, err := bot.GetUserProfile([]string{content.From})
			if err != nil {
				return
			}
			text, err := content.TextContent()
			if err != nil {
				log.Println(err)
			}
			switch text.Text {
			case "加入":
				if user.Count == 1 {
					c := db.Connect(os.Getenv("REDISTOGO_URL"))
					n, appendErr := c.Do("SADD", "user", content.From)
					if appendErr != nil {
						log.Println("SET to redis error", appendErr, n)
					} else {
						_, err = bot.SendText([]string{content.From}, user.Contacts[0].DisplayName+" 您好，已將您加入傳送對象 ^＿^ "+content.From)
						if err != nil {
							log.Println(err)
						}
					}
					defer c.Close()
				}
			case "退出":
				if user.Count == 1 {
					c := db.Connect(os.Getenv("REDISTOGO_URL"))
					n, setErr := c.Do("SREM", "user", user.Contacts[0].MID)
					if setErr != nil {
						log.Println("DEL to redis error", setErr, n)
					} else {
						_, err = bot.SendText([]string{content.From}, user.Contacts[0].DisplayName+" 掰掰 Q＿Q")
						if err != nil {
							log.Println(err)
						}
					}
					defer c.Close()
				}
			case "狀態":
				if user.Count == 1 {
					c := db.Connect(os.Getenv("REDISTOGO_URL"))
					status, getErr := redis.Int(c.Do("SISMEMBER", user.Contacts[0].MID))
					if getErr != nil || status == 0 {
						_, err = bot.SendText([]string{content.From}, "目前沒有登記您的編號喔！")
						if err != nil {
							log.Println(err)
						}
					} else {
						_, err = bot.SendText([]string{content.From}, user.Contacts[0].MID)
						if err != nil {
							log.Println(err)
						}
					}
					defer c.Close()
				}
			}
		}
	}
}
<commit_msg>changed folder name<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/garyburd/redigo/redis"
	"github.com/lancetw/hcfd-forecast/db"
	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	strID := os.Getenv("ChannelID")
	numID, err := strconv.ParseInt(strID, 10, 64)
	if err != nil {
		log.Fatal("Wrong environment setting about ChannelID")
	}

	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()
		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {
			user, err := bot.GetUserProfile([]string{content.From})
			if err != nil {
				return
			}
			text, err := content.TextContent()
			if err != nil {
				log.Println(err)
			}
			switch text.Text {
			case "加入":
				if user.Count == 1 {
					c := db.Connect(os.Getenv("REDISTOGO_URL"))
					n, appendErr := c.Do("SADD", "user", content.From)
					if appendErr != nil {
						log.Println("SET to redis error", appendErr, n)
					} else {
						_, err = bot.SendText([]string{content.From}, user.Contacts[0].DisplayName+" 您好，已將您加入傳送對象 ^＿^ "+content.From)
						if err != nil {
							log.Println(err)
						}
					}
					defer c.Close()
				}
			case "退出":
				if user.Count == 1 {
					c := db.Connect(os.Getenv("REDISTOGO_URL"))
					n, setErr := c.Do("SREM", "user", user.Contacts[0].MID)
					if setErr != nil {
						log.Println("DEL to redis error", setErr, n)
					} else {
						_, err = bot.SendText([]string{content.From}, user.Contacts[0].DisplayName+" 掰掰 Q＿Q")
						if err != nil {
							log.Println(err)
						}
					}
					defer c.Close()
				}
			case "狀態":
				if user.Count == 1 {
					c := db.Connect(os.Getenv("REDISTOGO_URL"))
					status, getErr := redis.Int(c.Do("SISMEMBER", user.Contacts[0].MID))
					if getErr != nil || status == 0 {
						_, err = bot.SendText([]string{content.From}, "目前沒有登記您的編號喔！")
						if err != nil {
							log.Println(err)
						}
					} else {
						_, err = bot.SendText([]string{content.From}, user.Contacts[0].MID)
						if err != nil {
							log.Println(err)
						}
					}
					defer c.Close()
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
    "code.google.com/p/go-uuid/uuid"
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "github.com/ory-am/common/env"
    "github.com/ory-am/gitdeploy/flynn"
    "github.com/ory-am/gitdeploy/github"
    "github.com/ory-am/gitdeploy/sse"
    "github.com/ory-am/google-json-style-response/responder"
    "log"
    "net/http"
    "net/url"
    "os"
    "runtime"
    "os/exec"
    "time"
    "database/sql"
    _ "github.com/lib/pq"
    "io/ioutil"
)

const apiVersion = "1.0"

type deployRequest struct {
    Repository string `json:"repository"`
}

type pipe struct {}

func (p *pipe) Write(d []byte) (n int, err error) {
    log.Printf("Git subcommand: %s", d)
    return len(d), nil
}

func main() {
    pgPath := env.Getenv("PGPATH", "")
    if len(pgPath) < 1 {
        pgHost := env.Getenv("PGHOST", "localhost")
        pgDatabase := env.Getenv("PGDATABASE", "gitdeploy")
        pgUser := env.Getenv("PGUSER", "gitdeploy")
        pgPassword := env.Getenv("PGPASSWORD", "changeme")
        pgPath = fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=disable", pgUser, pgPassword, pgHost, pgDatabase)
    }
    host := env.Getenv("HOST", "")
    port := env.Getenv("PORT", "7654")
    listen := fmt.Sprintf("%s:%s", host, port)

    _ = initPg(pgPath)

    checkDependencies()

    // SSE broker
    sseBroker := sse.New()
    sseBroker.Start()

    // Mux router
    r := mux.NewRouter()
    r.HandleFunc("/deployments", deployWrapperAction(sseBroker)).Methods("POST")
    r.HandleFunc("/deployments", func(w http.ResponseWriter, r *http.Request) {
        // TODO This should be done differently. In some middleware e.g.
        w.Header().Add("Access-Control-Allow-Methods", "POST")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
    }).Methods("OPTIONS")
    r.HandleFunc("/deployments/{app:.+}/events", sseBroker.EventHandler).Methods("GET")
    r.PathPrefix("/").Handler(http.FileServer(http.Dir("./app/")))
    http.Handle("/", r)

    go cleanApps()

    log.Printf("Listening on %s", listen)
    log.Fatal(http.ListenAndServe(listen, nil))
}

func initPg(pgPath string) *sql.DB {
    db, err := sql.Open("postgres", pgPath)
    if err != nil {
        log.Fatalf("Could not connect to pg: %s", err)
    }
    dat, err := ioutil.ReadFile("./schema.sql")
    if err != nil {
        log.Fatal(err)
    }
    q := string(dat)
    _, err = db.Exec(q)
    if err != nil {
        log.Fatal(err)
    }
    return db
}

func cleanApps() {
    for {
        log.Println("Cleaning up apps...")
        time.Sleep(60 * time.Second)
    }
}

func checkDependencies() {
    go func() {
        if !github.Exists() {
            log.Fatal("Git CLI is required but not installed or not in path.")
        }
    }()
    go func() {
        if !flynn.Exists() {
            log.Fatal("Flynn CLI is required but not installed or not in path.")
        }
    }()
    go func() {
        _, err := exec.LookPath("bash")
        if err != nil {
            log.Fatal("Bash is required but not installed or not in path.")
        }
    }()
}

func deployWrapperAction(sseBroker *sse.Broker) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        deployAction(w, r, sseBroker)
    }
}

func deployAction(w http.ResponseWriter, r *http.Request, sseBroker *sse.Broker) {
    // TODO This should be done differently. In some middleware e.g.
    w.Header().Add("Access-Control-Allow-Methods", "POST")
    w.Header().Set("Access-Control-Allow-Origin", "*")
    response := responder.New(apiVersion)
    dr := new(deployRequest)
    decoder := json.NewDecoder(r.Body)
    decoder.Decode(dr)
    app := uuid.NewRandom().String()
    c := sseBroker.OpenChannel(app)

    if len(dr.Repository) < 1 {
        re := response.Error(http.StatusBadRequest, "No repository provided.")
        response.Write(w, re)
        return
    }

    url, err := url.Parse(dr.Repository)
    if err != nil {
        re := response.Error(http.StatusInternalServerError, err.Error())
        response.Write(w, re)
        return
    }

    if url.Host != "github.com" {
        re := response.Error(http.StatusBadRequest, "I only support GitHub.")
        response.Write(w, re)
        return
    }

    re := response.Success(struct {
        App   string `json:"app"`
    }{
        App: app,
    })
    response.Write(w, re)

    go func() {
        defer sseBroker.CloseChannel(app)
        dest, err := cloneRepository(dr.Repository, app, c)
        log.Print("Cloning repository was successful");
        c.Messages <- "Cloning repository was successful"
        if err != nil {
            re := response.Error(http.StatusInternalServerError, err.Error())
            response.Write(w, re)
            return
        }
        err = flynn.Deploy(dest, app, c)
        if err != nil {
            re := response.Error(http.StatusInternalServerError, err.Error())
            response.Write(w, re)
            return
        }
        // TODO fix host
        c.Messages <- fmt.Sprintf("Deployment successful. Your app is now available at %s.ew2.flynnhub.com", app)
    }()
}

func cloneRepository(repository string, app string, c *sse.Channel) (string, error) {
    dest := fmt.Sprintf("%s/%s", os.TempDir(), app)
    if runtime.GOOS == "windows" {
        dest = fmt.Sprintf("%s\\%s", os.TempDir(), app)
    }

    l := fmt.Sprintf("Creating tempdir %s", dest)
    log.Println(l)
    c.Messages <- l
    return dest, github.Clone(repository, dest, c)
}
<commit_msg>Minor changes<commit_after>package main

import (
    "code.google.com/p/go-uuid/uuid"
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "github.com/ory-am/common/env"
    "github.com/ory-am/gitdeploy/flynn"
    "github.com/ory-am/gitdeploy/github"
    "github.com/ory-am/gitdeploy/sse"
    "github.com/ory-am/google-json-style-response/responder"
    "log"
    "net/http"
    "net/url"
    "os"
    "runtime"
    "os/exec"
    "time"
    "database/sql"
    _ "github.com/lib/pq"
    "io/ioutil"
)

const apiVersion = "1.0"

type deployRequest struct {
    Repository string `json:"repository"`
}

type pipe struct {}

func (p *pipe) Write(d []byte) (n int, err error) {
    log.Printf("Git subcommand: %s", d)
    return len(d), nil
}

func main() {
    pgPath := env.Getenv("PGPATH", "")
    if len(pgPath) < 1 {
        pgHost := env.Getenv("PGHOST", "localhost")
        pgDatabase := env.Getenv("PGDATABASE", "gitdeploy")
        pgUser := env.Getenv("PGUSER", "gitdeploy")
        pgPassword := env.Getenv("PGPASSWORD", "changeme")
        pgPath = fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=disable", pgUser, pgPassword, pgHost, pgDatabase)
    }
    host := env.Getenv("HOST", "")
    port := env.Getenv("PORT", "7654")
    listen := fmt.Sprintf("%s:%s", host, port)

    _ = initPg(pgPath)

    checkDependencies()

    // SSE broker
    sseBroker := sse.New()
    sseBroker.Start()

    // Mux router
    r := mux.NewRouter()
    r.HandleFunc("/deployments", deployWrapperAction(sseBroker)).Methods("POST")
    r.HandleFunc("/deployments", func(w http.ResponseWriter, r *http.Request) {
        // TODO This should be done differently. In some middleware e.g.
        w.Header().Add("Access-Control-Allow-Methods", "POST")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
    }).Methods("OPTIONS")
    r.HandleFunc("/deployments/{app:.+}/events", sseBroker.EventHandler).Methods("GET")
    r.PathPrefix("/").Handler(http.FileServer(http.Dir("./app/")))
    http.Handle("/", r)

    go cleanApps()

    log.Printf("Listening on %s", listen)
    log.Fatal(http.ListenAndServe(listen, nil))
}

func initPg(pgPath string) *sql.DB {
    log.Println("Connecting to postgres...")
    db, err := sql.Open("postgres", pgPath)
    if err != nil {
        log.Fatalf("Could not connect to pg: %s", err)
    }
    dat, err := ioutil.ReadFile("./schema.sql")
    if err != nil {
        log.Fatal(err)
    }
    q := string(dat)
    _, err = db.Exec(q)
    if err != nil {
        log.Fatal(err)
    }
    return db
}

func cleanApps() {
    for {
        log.Println("Cleaning up apps...")
        time.Sleep(60 * time.Second)
    }
}

func checkDependencies() {
    go func() {
        if !github.Exists() {
            log.Fatal("Git CLI is required but not installed or not in path.")
        }
    }()
    go func() {
        if !flynn.Exists() {
            log.Fatal("Flynn CLI is required but not installed or not in path.")
        }
    }()
    go func() {
        _, err := exec.LookPath("bash")
        if err != nil {
            log.Fatal("Bash is required but not installed or not in path.")
        }
    }()
}

func deployWrapperAction(sseBroker *sse.Broker) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        deployAction(w, r, sseBroker)
    }
}

func deployAction(w http.ResponseWriter, r *http.Request, sseBroker *sse.Broker) {
    // TODO This should be done differently. In some middleware e.g.
    w.Header().Add("Access-Control-Allow-Methods", "POST")
    w.Header().Set("Access-Control-Allow-Origin", "*")
    response := responder.New(apiVersion)
    dr := new(deployRequest)
    decoder := json.NewDecoder(r.Body)
    decoder.Decode(dr)
    app := uuid.NewRandom().String()
    c := sseBroker.OpenChannel(app)

    if len(dr.Repository) < 1 {
        re := response.Error(http.StatusBadRequest, "No repository provided.")
        response.Write(w, re)
        return
    }

    url, err := url.Parse(dr.Repository)
    if err != nil {
        re := response.Error(http.StatusInternalServerError, err.Error())
        response.Write(w, re)
        return
    }

    if url.Host != "github.com" {
        re := response.Error(http.StatusBadRequest, "I only support GitHub.")
        response.Write(w, re)
        return
    }

    re := response.Success(struct {
        App   string `json:"app"`
    }{
        App: app,
    })
    response.Write(w, re)

    go func() {
        defer sseBroker.CloseChannel(app)
        dest, err := cloneRepository(dr.Repository, app, c)
        log.Print("Cloning repository was successful");
        c.Messages <- "Cloning repository was successful"
        if err != nil {
            re := response.Error(http.StatusInternalServerError, err.Error())
            response.Write(w, re)
            return
        }
        err = flynn.Deploy(dest, app, c)
        if err != nil {
            re := response.Error(http.StatusInternalServerError, err.Error())
            response.Write(w, re)
            return
        }
        // TODO fix host
        c.Messages <- fmt.Sprintf("Deployment successful. Your app is now available at %s.ew2.flynnhub.com", app)
    }()
}

func cloneRepository(repository string, app string, c *sse.Channel) (string, error) {
    dest := fmt.Sprintf("%s/%s", os.TempDir(), app)
    if runtime.GOOS == "windows" {
        dest = fmt.Sprintf("%s\\%s", os.TempDir(), app)
    }

    l := fmt.Sprintf("Creating tempdir %s", dest)
    log.Println(l)
    c.Messages <- l
    return dest, github.Clone(repository, dest, c)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/hadalhw17/AlexBot/commands"
)

func init() {
	flag.StringVar(&token, "t", "", "Bot Token")
	flag.Parse()
	dontDie()
}

const ANNOUNCEID  = "238048572413575168"
var token string

func main() {
	if token == "" {
		fmt.Println("No token provided. Please run: AlexBot -t <bot token>")
		return
	}

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		fmt.Println("Error creating Discord session: ", err)
		return
	}
	//dg.GuildMemberRoleRemove("238048572413575168","299602264039882752","295907725022593024")

	// Register ready as a callback for the ready events.
	dg.AddHandler(ready)

	// Register messageCreate as a callback for the messageCreate events.
	dg.AddHandler(messageCreate)

	// Register guildCreate as a callback for the guildCreate events.
	dg.AddHandler(guildCreate)

	//Register newMember as a callback for the GuildMemberAdd
	dg.AddHandler(newMember)
	//
	// Open the websocket and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("Error opening Discord session: ", err)
	}
	//Every 12 hours it will remind about courseworks
	ticker:=time.NewTicker(time.Hour * 12)
	var msg string
	var num int
	msg,num = commands.ReadCal("courseworks.ics")
	go func(){
		for range ticker.C {
			if num!= 0 {
				dg.ChannelMessageSend(ANNOUNCEID, "@everyone you have a lot of courseworks to do!"+"\n"+msg)
			}else {
				dg.ChannelMessageSend(ANNOUNCEID, " no courseworks!! You are free!")
			}
		}

	}()
	ticker1:=time.NewTicker(time.Minute * 10)
	go func(){
		for range ticker1.C {
			fmt.Println("...")
		}
	}()
	fmt.Print("Bot is running.... Thanks god!!")
	// Simple way to keep program running until CTRL-C is pressed.
	<-make(chan struct{})
	return
}

func ready(s *discordgo.Session, event *discordgo.Ready) {
	// Set the playing status.
	_ = s.UpdateStatus(0, "@AlexBot !info|HadalHW17")

}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if m.Author.ID == s.State.User.ID {
		// Ignore self
		return
	}

	reply, isExist:= commands.ParceForCommands(s,m)
	if isExist{
		s.ChannelMessageSend(m.ChannelID, reply)
	}
	return
}

// This function will be called (due to AddHandler above) every time a new
// guild is joined.+
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
	if event.Guild.Unavailable {
		return
	}

	for _, channel := range event.Guild.Channels {
		if channel.ID == event.Guild.ID {
		_, _ = s.ChannelMessageSend(ANNOUNCEID, "AlexBot is ready! Type !help to get some usefull shit about me.")
			return
		}
	}
}

// This function will be called (due to AddHandler above) every time a new
// member is joined.+
func newMember(s *discordgo.Session, event *discordgo.GuildMemberAdd){
	s.ChannelMessageSend(ANNOUNCEID, "New member joined. Welcome @" + event.Member.User.Username)

}

func dontDie(){
	ticker:=time.NewTicker(time.Minute * 20)
	go func(){
		for range ticker.C {
			fmt.Println("...")
		}
	}()
}
<commit_msg>Reduced the spam rate.<commit_after>package main

import (
	"flag"
	"fmt"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/hadalhw17/AlexBot/commands"
)

func init() {
	flag.StringVar(&token, "t", "", "Bot Token")
	flag.Parse()
	dontDie()
}

const ANNOUNCEID = "238048572413575168"

var token string

func main() {
	if token == "" {
		fmt.Println("No token provided. Please run: AlexBot -t <bot token>")
		return
	}

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		fmt.Println("Error creating Discord session: ", err)
		return
	}
	//dg.GuildMemberRoleRemove("238048572413575168","299602264039882752","295907725022593024")

	// Register ready as a callback for the ready events.
	dg.AddHandler(ready)

	// Register messageCreate as a callback for the messageCreate events.
	dg.AddHandler(messageCreate)

	// Register guildCreate as a callback for the guildCreate events.
	dg.AddHandler(guildCreate)

	//Register newMember as a callback for the GuildMemberAdd
	dg.AddHandler(newMember)
	//
	// Open the websocket and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("Error opening Discord session: ", err)
	}
	//Every 7 days, the bot will send a reminder about our pending coursework.
	ticker := time.NewTicker(time.Day * 7)
	var msg string
	var num int
	msg, num = commands.ReadCal("courseworks.ics")
	go func() {
		for range ticker.C {
			if num != 0 {
				dg.ChannelMessageSend(ANNOUNCEID, "you have a lot of courseworks to do!"+"\n"+msg)
			} else {
				dg.ChannelMessageSend(ANNOUNCEID, " no courseworks!! You are free!")
			}
		}

	}()
	ticker1 := time.NewTicker(time.Minute * 10)
	go func() {
		for range ticker1.C {
			fmt.Println("...")
		}
	}()
	fmt.Print("Bot is running.... Thanks god!!")
	// Simple way to keep program running until CTRL-C is pressed.
	<-make(chan struct{})
	return
}

func ready(s *discordgo.Session, event *discordgo.Ready) {
	// Set the playing status.
	_ = s.UpdateStatus(0, "@AlexBot !info|HadalHW17")

}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if m.Author.ID == s.State.User.ID {
		// Ignore self
		return
	}

	reply, isExist := commands.ParceForCommands(s, m)
	if isExist {
		s.ChannelMessageSend(m.ChannelID, reply)
	}
	return
}

// This function will be called (due to AddHandler above) every time a new
// guild is joined.+
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
	if event.Guild.Unavailable {
		return
	}

	for _, channel := range event.Guild.Channels {
		if channel.ID == event.Guild.ID {
			_, _ = s.ChannelMessageSend(ANNOUNCEID, "AlexBot is ready! Type !help to get some usefull shit about me.")
			return
		}
	}
}

// This function will be called (due to AddHandler above) every time a new
// member is joined.+
func newMember(s *discordgo.Session, event *discordgo.GuildMemberAdd) {
	s.ChannelMessageSend(ANNOUNCEID, "New member joined. Welcome @"+event.Member.User.Username)

}

func dontDie() {
	ticker := time.NewTicker(time.Minute * 20)
	go func() {
		for range ticker.C {
			fmt.Println("...")
		}
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"sort"
	"strings"
	"sync"
	"time"

	ui "github.com/gizak/termui"
	"github.com/samalba/dockerclient"
)

var (
	dockersocket          = flag.String("docker", "unix:///var/run/docker.sock", "the socket of the docker daemon")
	allcontainers         []dockerclient.Container
	containerDetailsIndex = 0
	containerDetailsId    = ""
	statsData             = make(map[string][]*dockerclient.Stats)
	columns               = 0
	lock                  sync.Mutex
)

type DockerDrawer func(*dockerclient.DockerClient)

func dockerStats(id string, stats *dockerclient.Stats, errs chan error, data ...interface{}) {
	lock.Lock()
	defer lock.Unlock()
	dat, _ := statsData[id]
	if len(dat) > (columns - 2) {
		dat = dat[1:]
	}
	if len(dat) > 0 && dat[len(dat)-1].Read == stats.Read {
		// same stat twice, ignore
		return
	}
	dat = append(dat, stats)
	statsData[id] = dat
}

func ContainerList() (DockerDrawer, ui.GridBufferer) {
	list := ui.NewList()
	list.ItemFgColor = ui.ColorYellow
	list.Border.Label = "Containers"
	return func(dc *dockerclient.DockerClient) {
		containers, err := dc.ListContainers(false, false, "")
		if err != nil {
			log.Printf(err.Error())
		} else {
			var conts []string
			newstats := make(map[string][]*dockerclient.Stats)
			for i, c := range containers {
				conts = append(conts, genContainerListName(i, c, 30))
				if i == containerDetailsIndex {
					containerDetailsId = c.Id
				}
				stat, ok := statsData[c.Id]
				if ok {
					newstats[c.Id] = stat
				} else {
					errs := make(chan error)
					dc.StartMonitorStats(c.Id, dockerStats, errs, &c)
				}
			}
			lock.Lock()
			defer lock.Unlock()
			statsData = newstats
			allcontainers = containers
			list.Items = conts
			list.Height = len(conts) + 2
		}
	}, list
}

func genContainerListName(idx int, c dockerclient.Container, maxlen int) string {
	s := fmt.Sprintf("[%d] %s:%s", idx, c.Names[0], c.Id)
	if len(s) > maxlen {
		return s[:maxlen-3] + "..."
	}
	return s
}

func ContainerDetails() (DockerDrawer, ui.GridBufferer) {
	list := ui.NewList()
	list.ItemFgColor = ui.ColorYellow
	list.Border.Label = "Details"
	return func(dc *dockerclient.DockerClient) {
		if containerDetailsId == "" {
			return
		}
		ci, err := dc.InspectContainer(containerDetailsId)
		if err != nil {
			log.Printf(err.Error())
		} else {
			var lines []string
			lines = append(lines, fmt.Sprintf("Name: %s", ci.Name))
			lines = append(lines, fmt.Sprintf("Image: %s", ci.Image))
			lines = append(lines, fmt.Sprintf("Path: %s", ci.Path))
			lines = append(lines, fmt.Sprintf("Args: %s", ci.Args))
			lines = append(lines, fmt.Sprintf("IP: %s", ci.NetworkSettings.IPAddress))
			lines = append(lines, fmt.Sprintf("Ports: %s", genPortMappings(ci)))
			for vi, v := range genVolumes(ci) {
				if vi == 0 {
					lines = append(lines, fmt.Sprintf("Volumes: %s", v))
				} else {
					lines = append(lines, fmt.Sprintf("         %s", v))
				}
			}
			lines = append(lines, fmt.Sprintf("Hostname: %s", ci.Config.Hostname))
			lines = append(lines, fmt.Sprintf("Memory: %d", ci.Config.Memory))
			lines = append(lines, fmt.Sprintf("Swap: %d", ci.Config.MemorySwap))
			lines = append(lines, fmt.Sprintf("Cpu-Shares: %d", ci.Config.CpuShares))
			lines = append(lines, fmt.Sprintf("Cpu-Set: %s", ci.Config.Cpuset))
			lines = append(lines, fmt.Sprintf("Env: %s", ci.Config.Env))
			list.Items = lines
			list.Height = len(lines) + 2
			list.Border.Label = fmt.Sprintf("Details: %s", ci.Name)
		}
	}, list
}

func genPortMappings(di *dockerclient.ContainerInfo) string {
	var res []string
	var keys []string
	for p, _ := range di.NetworkSettings.Ports {
		keys = append(keys, p)
	}
	sort.Strings(keys)
	for _, k := range keys {
		pc := di.NetworkSettings.Ports[k]
		res = append(res, fmt.Sprintf("%s -> %s ", k, pc))
	}

	return strings.Join(res, ",")
}

func genVolumes(di *dockerclient.ContainerInfo) []string {
	var res []string
	var keys []string
	for v, _ := range di.Volumes {
		keys = append(keys, v)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := di.Volumes[k]
		res = append(res, fmt.Sprintf("%s -> %s ", k, v))
	}
	return res
}

func ContainerCpu() (DockerDrawer, ui.GridBufferer) {
	cpus := ui.NewSparklines()
	cpus.Border.Label = "CPU"
	return func(dc *dockerclient.DockerClient) {
		cpus.Lines = []ui.Sparkline{}
		cpus.Height = 2
		for _, c := range allcontainers {
			dat, _ := statsData[c.Id]
			lastVal := 0
			if len(dat) > 1 {
				lastVal = cpuPercent(dat, len(dat)-1)
			}
			l := ui.NewSparkline()
			l.Title = fmt.Sprintf("[%d %%] %s:%s ", lastVal, c.Names, c.Id)
			l.LineColor = ui.ColorRed
			l.Data = genCPUSystemUsage(dat)
			cpus.Lines = append(cpus.Lines, l)
			cpus.Height = cpus.Height + 2
		}

	}, cpus
}

func genCPUSystemUsage(stats []*dockerclient.Stats) []int {
	var res []int
	for i, _ := range stats {
		if i > 0 {
			res = append(res, cpuPercent(stats, i))
		}
	}
	return res
}

func cpuPercent(stats []*dockerclient.Stats, idx int) int {
	var (
		p        = 0.0
		mystat   = stats[idx]
		prevstat = stats[idx-1]
		cpudelta = float64(mystat.CpuStats.CpuUsage.TotalUsage - prevstat.CpuStats.CpuUsage.TotalUsage)
		sysdelta = float64(mystat.CpuStats.SystemUsage - prevstat.CpuStats.SystemUsage)
	)

	if sysdelta > 0.0 && cpudelta > 0.0 {
		p = (cpudelta / sysdelta) * float64(len(mystat.CpuStats.CpuUsage.PercpuUsage)) * 100.0
	}
	return int(p)
}

func main() {
	flag.Parse()

	err := ui.Init()
	if err != nil {
		panic(err)
	}
	defer ui.Close()

	// Init the client
	docker, err := dockerclient.NewDockerClient(*dockersocket, nil)
	if err != nil {
		panic(err)
	}

	var drawers []DockerDrawer
	containerlist, uiCntList := ContainerList()
	containerDetails, uiCntDets := ContainerDetails()
	cpuList, uiCpus := ContainerCpu()

	drawers = append(drawers, containerlist, containerDetails, cpuList)

	title := ui.NewPar("dockmon (press 'q' to quit)")
	title.Height = 3
	title.HasBorder = true

	ui.Body.AddRows(
		ui.NewRow(
			ui.NewCol(12, 0, title)),
		ui.NewRow(
			ui.NewCol(4, 0, uiCntList),
			ui.NewCol(8, 0, uiCntDets)),
		ui.NewRow(
			ui.NewCol(12, 0, uiCpus)))

	// calculate layout
	ui.Body.Align()
	columns = ui.Body.Width

	evt := ui.EventCh()

	for {
		select {
		case e := <-evt:
			if e.Type == ui.EventKey && e.Ch == 'q' {
				return
			}
			if e.Type == ui.EventKey && e.Ch >= '0' && e.Ch <= '9' {
				containerDetailsIndex = int(e.Ch - '0')
			}
			if e.Type == ui.EventResize {
				ui.Body.Width = ui.TermWidth()
				ui.Body.Align()
			}
		default:
			for _, d := range drawers {
				d(docker)
			}
			ui.Body.Align()
			columns = ui.Body.Width
			ui.Render(ui.Body)
			time.Sleep(time.Second / 2)
		}
	}
}
<commit_msg>dont log error to console while running dockmon<commit_after>package main

import (
	"flag"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	ui "github.com/gizak/termui"
	"github.com/samalba/dockerclient"
)

var (
	dockersocket          = flag.String("docker", "unix:///var/run/docker.sock", "the socket of the docker daemon")
	allcontainers         []dockerclient.Container
	containerDetailsIndex = 0
	containerDetailsId    = ""
	statsData             = make(map[string][]*dockerclient.Stats)
	columns               = 0
	lock                  sync.Mutex
)

type DockerDrawer func(*dockerclient.DockerClient)

func dockerStats(id string, stats *dockerclient.Stats, errs chan error, data ...interface{}) {
	lock.Lock()
	defer lock.Unlock()
	dat, _ := statsData[id]
	if len(dat) > (columns - 2) {
		dat = dat[1:]
	}
	if len(dat) > 0 && dat[len(dat)-1].Read == stats.Read {
		// same stat twice, ignore
		return
	}
	dat = append(dat, stats)
	statsData[id] = dat
}

func ContainerList() (DockerDrawer, ui.GridBufferer) {
	list := ui.NewList()
	list.ItemFgColor = ui.ColorYellow
	list.Border.Label = "Containers"
	return func(dc *dockerclient.DockerClient) {
		containers, err := dc.ListContainers(false, false, "")
		if err != nil {
			containerDetailsId = ""
			dc.StopAllMonitorStats()
			//log.Printf(err.Error())
		} else {
			var conts []string
			newstats := make(map[string][]*dockerclient.Stats)
			for i, c := range containers {
				conts = append(conts, genContainerListName(i, c, 30))
				if i == containerDetailsIndex {
					containerDetailsId = c.Id
				}
				stat, ok := statsData[c.Id]
				if ok {
					newstats[c.Id] = stat
				} else {
					errs := make(chan error)
					dc.StartMonitorStats(c.Id, dockerStats, errs, &c)
				}
			}
			lock.Lock()
			defer lock.Unlock()
			statsData = newstats
			allcontainers = containers
			if len(allcontainers) == 0 {
				dc.StopAllMonitorStats()
				containerDetailsId = ""
			}
			list.Items = conts
			list.Height = len(conts) + 2
		}
	}, list
}

func genContainerListName(idx int, c dockerclient.Container, maxlen int) string {
	s := fmt.Sprintf("[%d] %s:%s", idx, c.Names[0], c.Id)
	if len(s) > maxlen {
		return s[:maxlen-3] + "..."
	}
	return s
}

func ContainerDetails() (DockerDrawer, ui.GridBufferer) {
	list := ui.NewList()
	list.ItemFgColor = ui.ColorYellow
	list.Border.Label = "Details"
	return func(dc *dockerclient.DockerClient) {
		if containerDetailsId == "" {
			return
		}
		ci, err := dc.InspectContainer(containerDetailsId)
		if err != nil {
			//log.Printf(err.Error())
		} else {
			var lines []string
			lines = append(lines, fmt.Sprintf("Name: %s", ci.Name))
			lines = append(lines, fmt.Sprintf("Image: %s", ci.Image))
			lines = append(lines, fmt.Sprintf("Path: %s", ci.Path))
			lines = append(lines, fmt.Sprintf("Args: %s", ci.Args))
			lines = append(lines, fmt.Sprintf("IP: %s", ci.NetworkSettings.IPAddress))
			lines = append(lines, fmt.Sprintf("Ports: %s", genPortMappings(ci)))
			for vi, v := range genVolumes(ci) {
				if vi == 0 {
					lines = append(lines, fmt.Sprintf("Volumes: %s", v))
				} else {
					lines = append(lines, fmt.Sprintf("         %s", v))
				}
			}
			lines = append(lines, fmt.Sprintf("Hostname: %s", ci.Config.Hostname))
			lines = append(lines, fmt.Sprintf("Memory: %d", ci.Config.Memory))
			lines = append(lines, fmt.Sprintf("Swap: %d", ci.Config.MemorySwap))
			lines = append(lines, fmt.Sprintf("Cpu-Shares: %d", ci.Config.CpuShares))
			lines = append(lines, fmt.Sprintf("Cpu-Set: %s", ci.Config.Cpuset))
			lines = append(lines, fmt.Sprintf("Env: %s", ci.Config.Env))
			list.Items = lines
			list.Height = len(lines) + 2
			list.Border.Label = fmt.Sprintf("Details: %s", ci.Name)
		}
	}, list
}

func genPortMappings(di *dockerclient.ContainerInfo) string {
	var res []string
	var keys []string
	for p, _ := range di.NetworkSettings.Ports {
		keys = append(keys, p)
	}
	sort.Strings(keys)
	for _, k := range keys {
		pc := di.NetworkSettings.Ports[k]
		res = append(res, fmt.Sprintf("%s -> %s ", k, pc))
	}

	return strings.Join(res, ",")
}

func genVolumes(di *dockerclient.ContainerInfo) []string {
	var res []string
	var keys []string
	for v, _ := range di.Volumes {
		keys = append(keys, v)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := di.Volumes[k]
		res = append(res, fmt.Sprintf("%s -> %s ", k, v))
	}
	return res
}

func ContainerCpu() (DockerDrawer, ui.GridBufferer) {
	cpus := ui.NewSparklines()
	cpus.Border.Label = "CPU"
	return func(dc *dockerclient.DockerClient) {
		cpus.Lines = []ui.Sparkline{}
		cpus.Height = 2
		for _, c := range allcontainers {
			dat, _ := statsData[c.Id]
			lastVal := 0
			if len(dat) > 1 {
				lastVal = cpuPercent(dat, len(dat)-1)
			}
			l := ui.NewSparkline()
			l.Title = fmt.Sprintf("[%d %%] %s:%s ", lastVal, c.Names, c.Id)
			l.LineColor = ui.ColorRed
			l.Data = genCPUSystemUsage(dat)
			cpus.Lines = append(cpus.Lines, l)
			cpus.Height = cpus.Height + 2
		}

	}, cpus
}

func genCPUSystemUsage(stats []*dockerclient.Stats) []int {
	var res []int
	for i, _ := range stats {
		if i > 0 {
			res = append(res, cpuPercent(stats, i))
		}
	}
	return res
}

func cpuPercent(stats []*dockerclient.Stats, idx int) int {
	var (
		p        = 0.0
		mystat   = stats[idx]
		prevstat = stats[idx-1]
		cpudelta = float64(mystat.CpuStats.CpuUsage.TotalUsage - prevstat.CpuStats.CpuUsage.TotalUsage)
		sysdelta = float64(mystat.CpuStats.SystemUsage - prevstat.CpuStats.SystemUsage)
	)

	if sysdelta > 0.0 && cpudelta > 0.0 {
		p = (cpudelta / sysdelta) * float64(len(mystat.CpuStats.CpuUsage.PercpuUsage)) * 100.0
	}
	return int(p)
}

func main() {
	flag.Parse()

	err := ui.Init()
	if err != nil {
		panic(err)
	}
	defer ui.Close()

	// Init the client
	docker, err := dockerclient.NewDockerClient(*dockersocket, nil)
	if err != nil {
		panic(err)
	}

	var drawers []DockerDrawer
	containerlist, uiCntList := ContainerList()
	containerDetails, uiCntDets := ContainerDetails()
	cpuList, uiCpus := ContainerCpu()

	drawers = append(drawers, containerlist, containerDetails, cpuList)

	title := ui.NewPar("dockmon (press 'q' to quit)")
	title.Height = 3
	title.HasBorder = true

	ui.Body.AddRows(
		ui.NewRow(
			ui.NewCol(12, 0, title)),
		ui.NewRow(
			ui.NewCol(4, 0, uiCntList),
			ui.NewCol(8, 0, uiCntDets)),
		ui.NewRow(
			ui.NewCol(12, 0, uiCpus)))

	// calculate layout
	ui.Body.Align()
	columns = ui.Body.Width

	evt := ui.EventCh()

	for {
		select {
		case e := <-evt:
			if e.Type == ui.EventKey && e.Ch == 'q' {
				return
			}
			if e.Type == ui.EventKey && e.Ch >= '0' && e.Ch <= '9' {
				containerDetailsIndex = int(e.Ch - '0')
			}
			if e.Type == ui.EventResize {
				ui.Body.Width = ui.TermWidth()
				ui.Body.Align()
			}
		default:
			for _, d := range drawers {
				d(docker)
			}
			ui.Body.Align()
			columns = ui.Body.Width
			ui.Render(ui.Body)
			time.Sleep(time.Second / 2)
		}
	}
}
<|endoftext|>