<commit_before>// netbackup - Consistent multi-method backup tool
//
// See instructions in the README.md file that accompanies this program.
//
// (C) 2015 by Marco Paganini <paganini AT paganini DOT net>

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/marcopaganini/logger"
	"github.com/marcopaganini/netbackup/config"
	"github.com/marcopaganini/netbackup/execute"
	"github.com/marcopaganini/netbackup/transports"
)

const (
	defaultLogDir = "/tmp/log/netbackup"
	devMapperDir  = "/dev/mapper"

	// Default permissions for log directories and files.
	// The current umask will apply to these.
	defaultLogDirMode  = 0777
	defaultLogFileMode = 0666

	// Return codes
	osSuccess = 0
	osError   = 1

	// External commands.
	mountCmd      = "mount"
	umountCmd     = "umount"
	cryptSetupCmd = "cryptsetup"
	fsckCmd       = "fsck"
	tunefsCmd     = "tune2fs"
	dfCmd         = "df"
)

// Backup contains information for a given backup instance.
type Backup struct {
	log     *logger.Logger
	config  *config.Config
	outLog  *os.File
	verbose int
	dryRun  bool
}

var (
	// Generic logging object
	log *logger.Logger

	// Output Log
	outLog *os.File = os.Stderr
)

// NewBackup creates a new Backup instance.
func NewBackup(log *logger.Logger, config *config.Config, outLog *os.File, verbose int, dryRun bool) *Backup {
	// Create new Backup and execute.
	return &Backup{
		log:     log,
		config:  config,
		outLog:  outLog,
		verbose: verbose,
		dryRun:  opt.dryrun}
}

// mountDev mounts the destination device into a temporary mount point and
// returns the mount point name.
func (b *Backup) mountDev() (string, error) {
	tmpdir, err := ioutil.TempDir("", "netbackup_mount")
	if err != nil {
		return "", fmt.Errorf("unable to create temp directory: %v", err)
	}

	// We use the mount command instead of the mount syscall as it makes
	// simpler to specify defaults in /etc/fstab.
	cmd := mountCmd + " " + b.config.DestDev + " " + tmpdir
	if err := runCommand("MOUNT", cmd, nil); err != nil {
		return "", err
	}

	return tmpdir, nil
}

// umountDev dismounts the destination device specified in config.DestDev.
func (b *Backup) umountDev() error {
	cmd := umountCmd + " " + b.config.DestDev
	return runCommand("UMOUNT", cmd, nil)
}

// openLuks opens the luks destination device into a temporary /dev/mapper
// device file and retuns the /dev/mapper device filename.
func (b *Backup) openLuks() (string, error) {
	// Our temporary dev/mapper device is based on the config name
	devname := "netbackup_" + b.config.Name
	devfile := filepath.Join(devMapperDir, devname)

	// Make sure it doesn't already exist
	if _, err := os.Stat(devfile); err == nil {
		return "", fmt.Errorf("device mapper file %q already exists", devfile)
	}

	// cryptsetup LuksOpen
	cmd := cryptSetupCmd
	if b.config.LuksKeyFile != "" {
		cmd += " --key-file " + b.config.LuksKeyFile
	}
	cmd += " luksOpen " + b.config.LuksDestDev + " " + devname
	if err := runCommand("LUKS_OPEN", cmd, nil); err != nil {
		return "", err
	}

	return devfile, nil
}

// closeLuks closes the current destination device.
func (b *Backup) closeLuks() error {
	// cryptsetup luksClose needs the /dev/mapper device name.
	cmd := cryptSetupCmd + " luksClose " + b.config.DestDev
	return runCommand("LUKS_CLOSE", cmd, nil)
}

// cleanFilesystem runs fsck to make sure the filesystem under config.dest_dev is
// intact, and sets the number of times to check to 0 and the last time
// checked to now. This option should only be used in EXTn filesystems or
// filesystems that support tunefs.
func (b *Backup) cleanFilesystem() error {
	// fsck (read-only check)
	cmd := fsckCmd + " -n " + b.config.DestDev
	if err := runCommand("FS_CLEANUP", cmd, nil); err != nil {
		return fmt.Errorf("error running %q: %v", cmd, err)
	}
	// Tunefs
	cmd = tunefsCmd + " -C 0 -T now " + b.config.DestDev
	return runCommand("FS_CLEANUP", cmd, nil)
}

// Run executes the backup according to the config file and options.
func (b *Backup) Run() error {
	var transp interface {
		Run() error
	}

	if !b.dryRun {
		// Open LUKS device, if needed
		if b.config.LuksDestDev != "" {
			devfile, err := b.openLuks()
			if err != nil {
				return fmt.Errorf("Error opening LUKS device %q: %v", b.config.LuksDestDev, err)
			}
			// Set the destination device to the /dev/mapper device opened by
			// LUKS. This should allow the natural processing to mount and
			// dismount this device.
			b.config.DestDev = devfile

			// close luks device at the end
			defer b.closeLuks()
			defer time.Sleep(2 * time.Second)
		}

		// Run cleanup on fs prior to backup, if requested.
		if b.config.FSCleanup {
			if err := b.cleanFilesystem(); err != nil {
				return fmt.Errorf("Error performing pre-backup cleanup on %q: %v", b.config.DestDev, err)
			}
		}

		// Mount destination device, if needed.
		if b.config.DestDev != "" {
			tmpdir, err := b.mountDev()
			if err != nil {
				return fmt.Errorf("Error opening destination device %q: %v", b.config.DestDev, err)
			}
			// After we mount the destination device, we set Destdir to that location
			// so the backup will proceed seamlessly.
			b.config.DestDir = tmpdir

			// umount destination filesystem and remove temp mount point.
			defer os.Remove(b.config.DestDir)
			defer b.umountDev()
			// For some reason, not having a pause before attempting to unmount
			// can generate a race condition where umount complains that the fs
			// is busy (even though the transport is already down.)
			defer time.Sleep(2 * time.Second)
		}
	}

	var err error

	// Create new transport based on config.Transport
	switch b.config.Transport {
	case "rclone":
		transp, err = transports.NewRcloneTransport(b.config, nil, b.outLog, b.verbose, b.dryRun)
	case "rdiff-backup":
		transp, err = transports.NewRdiffBackupTransport(b.config, nil, b.outLog, b.verbose, b.dryRun)
	case "rsync":
		transp, err = transports.NewRsyncTransport(b.config, nil, b.outLog, b.verbose, b.dryRun)
	default:
		return fmt.Errorf("Unknown transport %q", b.config.Transport)
	}
	if err != nil {
		return fmt.Errorf("Error creating %s transport: %v", b.config.Transport, err)
	}

	// Execute pre-commands, if any.
	if b.config.PreCommand != "" && !b.dryRun {
		if err := runCommand("PRE", b.config.PreCommand, nil); err != nil {
			return fmt.Errorf("Error running pre-command: %v", err)
		}
	}

	// Make it so...
	if err := transp.Run(); err != nil {
		return fmt.Errorf("Error running backup: %v", err)
	}

	// Execute post-commands, if any.
	if b.config.PostCommand != "" && !b.dryRun {
		if err := runCommand("POST", b.config.PostCommand, nil); err != nil {
			return fmt.Errorf("Error running post-command (possible backup failure): %v", err)
		}
	}

	return nil
}

// usage prints an error message and program usage to stderr, exiting after
// that.
func usage(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n\n", err)
	}
	fmt.Fprintf(os.Stderr, "Usage%s:\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(2)
}

// runCommand executes the given command using the shell. A prefix will
// be used to log the commands to the output log. Returns error.
func runCommand(prefix string, cmd string, ex *execute.Execute) error {
	m := fmt.Sprintf("%s Command: %q", prefix, cmd)
	log.Verboseln(1, m)

	// Create a new execute object, if current is nil
	e := ex
	if e == nil {
		e = execute.New()
	}

	// All streams copied to output log with "PRE:" as a prefix.
	e.SetStdout(func(buf string) error { _, err := fmt.Fprintf(outLog, "%s(stdout): %s\n", prefix, buf); return err })
	e.SetStderr(func(buf string) error { _, err := fmt.Fprintf(outLog, "%s(stderr): %s\n", prefix, buf); return err })

	// Run using shell
	shell := os.Getenv("SHELL")
	if shell == "" {
		shell = "/bin/sh"
	}
	err := e.Exec([]string{shell, "-c", "--", cmd})
	if err != nil {
		errmsg := fmt.Sprintf("%s returned: %v", prefix, err)
		log.Verbosef(1, "*** %s\n", errmsg)
		return fmt.Errorf(errmsg)
	}
	log.Verbosef(1, "%s returned: OK\n", prefix)
	return nil
}

// logPath constructs the name for the output log using the the name and
// the current system date.
func logPath(name string, logDir string) string {
	ymd := time.Now().Format("2006-01-02")
	dir := filepath.Join(logDir, name)
	return filepath.Join(dir, name+"-"+ymd+".log")
}

// logOpen opens (for append) or creates (if needed) the specified file.
// If the file doesn't exist, all intermediate directories will be created.
// Returns an *os.File to the just opened file.
func logOpen(path string) (*os.File, error) {
	// Create full directory path if it doesn't exist yet.
	dir := filepath.Dir(path)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err := os.MkdirAll(dir, defaultLogDirMode); err != nil {
			return nil, fmt.Errorf("unable to create dir tree %q: %v", dir, err)
		}
	}

	// Open for append or create if doesn't exist.
	w, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, defaultLogFileMode)
	if err != nil {
		return nil, fmt.Errorf("unable to open %q: %v", path, err)
	}
	return w, nil
}

// main
func main() {
	log = logger.New("")

	// Parse command line flags and read config file.
	if err := parseFlags(); err != nil {
		log.Printf("Command line error: %v", err)
		os.Exit(osError)
	}

	// Set verbose level
	verbose := int(opt.verbose)
	if verbose > 0 {
		log.SetVerboseLevel(verbose)
	}
	if opt.dryrun {
		log.Verbosef(2, "Warning: Dry-Run mode. Won't execute any commands.")
	}

	// Open and parse config file
	cfg, err := os.Open(opt.config)
	if err != nil {
		log.Printf("Unable to open config file: %v", err)
		os.Exit(osError)
	}
	config, err := config.ParseConfig(cfg)
	if err != nil {
		log.Printf("Configuration error in %q: %v", opt.config, err)
		os.Exit(osError)
	}

	// Create output log. Use the name specified in the config, if any,
	// or create a "standard" name using the backup name and date.
	logFilename := config.Logfile
	if logFilename == "" {
		logFilename = logPath(config.Name, defaultLogDir)
	}
	outLog, err := logOpen(logFilename)
	if err != nil {
		log.Printf("Unable to open/create logfile: %v", err)
		os.Exit(osError)
	}
	defer outLog.Close()

	// Configure log to log everything to stderr and outLog
	log.SetOutput([]*os.File{os.Stderr, outLog})

	// Create new Backup and execute.
	b := NewBackup(log, config, outLog, verbose, opt.dryrun)

	if err = b.Run(); err != nil {
		log.Println(err)
		os.Exit(osError)
	}
	log.Verboseln(1, "*** Backup Result: Success")
	os.Exit(osSuccess)
}
<commit_msg>First step towards sunsetting outLog.<commit_after>// netbackup - Consistent multi-method backup tool
//
// See instructions in the README.md file that accompanies this program.
//
// (C) 2015 by Marco Paganini <paganini AT paganini DOT net>

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/marcopaganini/logger"
	"github.com/marcopaganini/netbackup/config"
	"github.com/marcopaganini/netbackup/execute"
	"github.com/marcopaganini/netbackup/transports"
)

const (
	defaultLogDir = "/tmp/log/netbackup"
	devMapperDir  = "/dev/mapper"

	// Default permissions for log directories and files.
	// The current umask will apply to these.
	defaultLogDirMode  = 0777
	defaultLogFileMode = 0666

	// External commands.
	mountCmd      = "mount"
	umountCmd     = "umount"
	cryptSetupCmd = "cryptsetup"
	fsckCmd       = "fsck"
	tunefsCmd     = "tune2fs"
	dfCmd         = "df"
)

// Backup contains information for a given backup instance.
type Backup struct {
	log     *logger.Logger
	config  *config.Config
	outLog  *os.File
	verbose int
	dryRun  bool
}

var (
	// Generic logging object
	log *logger.Logger

	// Output Log
	outLog *os.File = os.Stderr
)

// NewBackup creates a new Backup instance.
func NewBackup(log *logger.Logger, config *config.Config, outLog *os.File, verbose int, dryRun bool) *Backup {
	// Create new Backup and execute.
	return &Backup{
		log:     log,
		config:  config,
		outLog:  outLog,
		verbose: verbose,
		dryRun:  opt.dryrun}
}

// mountDev mounts the destination device into a temporary mount point and
// returns the mount point name.
func (b *Backup) mountDev() (string, error) {
	tmpdir, err := ioutil.TempDir("", "netbackup_mount")
	if err != nil {
		return "", fmt.Errorf("unable to create temp directory: %v", err)
	}

	// We use the mount command instead of the mount syscall as it makes
	// simpler to specify defaults in /etc/fstab.
	cmd := mountCmd + " " + b.config.DestDev + " " + tmpdir
	if err := runCommand("MOUNT", cmd, nil); err != nil {
		return "", err
	}

	return tmpdir, nil
}

// umountDev dismounts the destination device specified in config.DestDev.
func (b *Backup) umountDev() error {
	cmd := umountCmd + " " + b.config.DestDev
	return runCommand("UMOUNT", cmd, nil)
}

// openLuks opens the luks destination device into a temporary /dev/mapper
// device file and retuns the /dev/mapper device filename.
func (b *Backup) openLuks() (string, error) {
	// Our temporary dev/mapper device is based on the config name
	devname := "netbackup_" + b.config.Name
	devfile := filepath.Join(devMapperDir, devname)

	// Make sure it doesn't already exist
	if _, err := os.Stat(devfile); err == nil {
		return "", fmt.Errorf("device mapper file %q already exists", devfile)
	}

	// cryptsetup LuksOpen
	cmd := cryptSetupCmd
	if b.config.LuksKeyFile != "" {
		cmd += " --key-file " + b.config.LuksKeyFile
	}
	cmd += " luksOpen " + b.config.LuksDestDev + " " + devname
	if err := runCommand("LUKS_OPEN", cmd, nil); err != nil {
		return "", err
	}

	return devfile, nil
}

// closeLuks closes the current destination device.
func (b *Backup) closeLuks() error {
	// cryptsetup luksClose needs the /dev/mapper device name.
	cmd := cryptSetupCmd + " luksClose " + b.config.DestDev
	return runCommand("LUKS_CLOSE", cmd, nil)
}

// cleanFilesystem runs fsck to make sure the filesystem under config.dest_dev is
// intact, and sets the number of times to check to 0 and the last time
// checked to now. This option should only be used in EXTn filesystems or
// filesystems that support tunefs.
func (b *Backup) cleanFilesystem() error {
	// fsck (read-only check)
	cmd := fsckCmd + " -n " + b.config.DestDev
	if err := runCommand("FS_CLEANUP", cmd, nil); err != nil {
		return fmt.Errorf("error running %q: %v", cmd, err)
	}
	// Tunefs
	cmd = tunefsCmd + " -C 0 -T now " + b.config.DestDev
	return runCommand("FS_CLEANUP", cmd, nil)
}

// Run executes the backup according to the config file and options.
func (b *Backup) Run() error {
	var transp interface {
		Run() error
	}

	if !b.dryRun {
		// Open LUKS device, if needed
		if b.config.LuksDestDev != "" {
			devfile, err := b.openLuks()
			if err != nil {
				return fmt.Errorf("Error opening LUKS device %q: %v", b.config.LuksDestDev, err)
			}
			// Set the destination device to the /dev/mapper device opened by
			// LUKS. This should allow the natural processing to mount and
			// dismount this device.
			b.config.DestDev = devfile

			// close luks device at the end
			defer b.closeLuks()
			defer time.Sleep(2 * time.Second)
		}

		// Run cleanup on fs prior to backup, if requested.
		if b.config.FSCleanup {
			if err := b.cleanFilesystem(); err != nil {
				return fmt.Errorf("Error performing pre-backup cleanup on %q: %v", b.config.DestDev, err)
			}
		}

		// Mount destination device, if needed.
		if b.config.DestDev != "" {
			tmpdir, err := b.mountDev()
			if err != nil {
				return fmt.Errorf("Error opening destination device %q: %v", b.config.DestDev, err)
			}
			// After we mount the destination device, we set Destdir to that location
			// so the backup will proceed seamlessly.
			b.config.DestDir = tmpdir

			// umount destination filesystem and remove temp mount point.
			defer os.Remove(b.config.DestDir)
			defer b.umountDev()
			// For some reason, not having a pause before attempting to unmount
			// can generate a race condition where umount complains that the fs
			// is busy (even though the transport is already down.)
			defer time.Sleep(2 * time.Second)
		}
	}

	var err error

	// Create new transport based on config.Transport
	switch b.config.Transport {
	case "rclone":
		transp, err = transports.NewRcloneTransport(b.config, nil, b.outLog, b.verbose, b.dryRun)
	case "rdiff-backup":
		transp, err = transports.NewRdiffBackupTransport(b.config, nil, b.outLog, b.verbose, b.dryRun)
	case "rsync":
		transp, err = transports.NewRsyncTransport(b.config, nil, b.outLog, b.verbose, b.dryRun)
	default:
		return fmt.Errorf("Unknown transport %q", b.config.Transport)
	}
	if err != nil {
		return fmt.Errorf("Error creating %s transport: %v", b.config.Transport, err)
	}

	// Execute pre-commands, if any.
	if b.config.PreCommand != "" && !b.dryRun {
		if err := runCommand("PRE", b.config.PreCommand, nil); err != nil {
			return fmt.Errorf("Error running pre-command: %v", err)
		}
	}

	// Make it so...
	if err := transp.Run(); err != nil {
		return fmt.Errorf("Error running backup: %v", err)
	}

	// Execute post-commands, if any.
	if b.config.PostCommand != "" && !b.dryRun {
		if err := runCommand("POST", b.config.PostCommand, nil); err != nil {
			return fmt.Errorf("Error running post-command (possible backup failure): %v", err)
		}
	}

	return nil
}

// usage prints an error message and program usage to stderr, exiting after
// that.
func usage(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n\n", err)
	}
	fmt.Fprintf(os.Stderr, "Usage%s:\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(2)
}

// runCommand executes the given command using the shell. A prefix will
// be used to log the commands to the output log. Returns error.
func runCommand(prefix string, cmd string, ex *execute.Execute) error {
	log.Verbosef(1, "%s Command: %q", prefix, cmd)

	// Create a new execute object, if current is nil
	e := ex
	if e == nil {
		e = execute.New()
	}

	// All streams copied to output log with "PRE:" as a prefix.
	e.SetStdout(func(buf string) error { log.Verbosef(3, "%s(stdout): %s\n", prefix, buf); return nil })
	e.SetStderr(func(buf string) error { log.Verbosef(3, "%s(stderr): %s\n", prefix, buf); return nil })

	// Run using shell
	shell := os.Getenv("SHELL")
	if shell == "" {
		shell = "/bin/sh"
	}
	err := e.Exec([]string{shell, "-c", "--", cmd})
	if err != nil {
		errmsg := fmt.Sprintf("%s returned: %v", prefix, err)
		log.Verbosef(1, "*** %s\n", errmsg)
		return fmt.Errorf(errmsg)
	}
	log.Verbosef(1, "%s returned: OK\n", prefix)
	return nil
}

// logPath constructs the name for the output log using the the name and
// the current system date.
func logPath(name string, logDir string) string {
	ymd := time.Now().Format("2006-01-02")
	dir := filepath.Join(logDir, name)
	return filepath.Join(dir, name+"-"+ymd+".log")
}

// logOpen opens (for append) or creates (if needed) the specified file.
// If the file doesn't exist, all intermediate directories will be created.
// Returns an *os.File to the just opened file.
func logOpen(path string) (*os.File, error) {
	// Create full directory path if it doesn't exist yet.
	dir := filepath.Dir(path)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err := os.MkdirAll(dir, defaultLogDirMode); err != nil {
			return nil, fmt.Errorf("unable to create dir tree %q: %v", dir, err)
		}
	}

	// Open for append or create if doesn't exist.
	w, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, defaultLogFileMode)
	if err != nil {
		return nil, fmt.Errorf("unable to open %q: %v", path, err)
	}
	return w, nil
}

// main
func main() {
	log = logger.New("")

	// Parse command line flags and read config file.
	if err := parseFlags(); err != nil {
		log.Fatalf("Command line error: %v", err)
	}

	// Set verbose level
	verbose := int(opt.verbose)
	if verbose > 0 {
		log.SetVerboseLevel(verbose)
	}
	if opt.dryrun {
		log.Verbosef(2, "Warning: Dry-Run mode. Won't execute any commands.")
	}

	// Open and parse config file
	cfg, err := os.Open(opt.config)
	if err != nil {
		log.Fatalf("Unable to open config file: %v", err)
	}
	config, err := config.ParseConfig(cfg)
	if err != nil {
		log.Fatalf("Configuration error in %q: %v", opt.config, err)
	}

	// Create output log. Use the name specified in the config, if any,
	// or create a "standard" name using the backup name and date.
	logFilename := config.Logfile
	if logFilename == "" {
		logFilename = logPath(config.Name, defaultLogDir)
	}
	outLog, err := logOpen(logFilename)
	if err != nil {
		log.Fatalf("Unable to open/create logfile: %v", err)
	}
	defer outLog.Close()

	// Configure log to log everything to stderr and outLog
	log.SetMirrorOutput(outLog)

	// Create new Backup and execute.
	b := NewBackup(log, config, outLog, verbose, opt.dryrun)

	if err = b.Run(); err != nil {
		log.Fatalln(err)
	}
	log.Verboseln(1, "*** Backup Result: Success")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"regexp"
)

func main() {
	filename := "test-input.json"
	raw, err := ioutil.ReadFile(filename)
	if err != nil {
		todo(err)
	}

	// The 'JSON' be an object, array or scalar,
	// so the best we can do for now is an empty interface type
	var top interface{}
	err = json.Unmarshal(raw, &top)
	if err != nil {
		todo(err)
	}

	ss, err := makeStatements("json", top)
	if err != nil {
		todo(err)
	}

	for _, s := range ss.statements {
		fmt.Println(s)
	}
}

func keyMustBeQuoted(s string) bool {
	r := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9]*$`)
	return !r.MatchString(s)
}

func makePrefix(prev string, next interface{}) (string, error) {
	switch v := next.(type) {
	case int:
		return fmt.Sprintf("%s[%d]", prev, v), nil
	case string:
		if keyMustBeQuoted(v) {
			return fmt.Sprintf("%s[%s]", prev, escape(v)), nil
		} else {
			return fmt.Sprintf("%s.%s", prev, v), nil
		}
	default:
		return "", fmt.Errorf("could not form prefix for %#v", next)
	}
}

type statementGroup struct {
	statements []string
}

func (s *statementGroup) Add(extra string) {
	s.statements = append(s.statements, extra)
}

func (s *statementGroup) AddGroup(g *statementGroup) {
	s.statements = append(s.statements, g.statements...)
}

func makeStatements(prefix string, v interface{}) (*statementGroup, error) {
	ss := &statementGroup{make([]string, 0)}

	switch vv := v.(type) {

	case map[string]interface{}:
		// It's an object
		ss.Add(fmt.Sprintf("%s = {};", prefix))

		for k, sub := range vv {
			newPrefix, err := makePrefix(prefix, k)
			if err != nil {
				return ss, err
			}
			extra, err := makeStatements(newPrefix, sub)
			if err != nil {
				return ss, err
			}
			ss.AddGroup(extra)
		}

	case []interface{}:
		// It's an array
		ss.Add(fmt.Sprintf("%s = [];", prefix))

		for k, sub := range vv {
			newPrefix, err := makePrefix(prefix, k)
			if err != nil {
				return ss, err
			}
			extra, err := makeStatements(newPrefix, sub)
			if err != nil {
				return ss, err
			}
			ss.AddGroup(extra)
		}

	case float64:
		ss.Add(fmt.Sprintf("%s = %s;", prefix, escape(vv)))

	case string:
		ss.Add(fmt.Sprintf("%s = %s;", prefix, escape(vv)))

	case bool:
		ss.Add(fmt.Sprintf("%s = %t;", prefix, vv))

	case nil:
		ss.Add(fmt.Sprintf("%s = null;", prefix))
	}

	return ss, nil
}

func escape(s interface{}) string {
	// I'm pretty sure it's safe to ignore this error
	// ...maybe. I'll work something into this I promise
	out, _ := json.Marshal(s)
	return string(out)
}

func todo(err error) {
	log.Fatalf("TODO: %s", err)
}
<commit_msg>Removes repeated Sprintf calls<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"regexp"
)

func main() {
	filename := "test-input.json"
	raw, err := ioutil.ReadFile(filename)
	if err != nil {
		todo(err)
	}

	// The 'JSON' be an object, array or scalar,
	// so the best we can do for now is an empty interface type
	var top interface{}
	err = json.Unmarshal(raw, &top)
	if err != nil {
		todo(err)
	}

	ss, err := makeStatements("json", top)
	if err != nil {
		todo(err)
	}

	for _, s := range ss.statements {
		fmt.Println(s)
	}
}

func keyMustBeQuoted(s string) bool {
	r := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9]*$`)
	return !r.MatchString(s)
}

func makePrefix(prev string, next interface{}) (string, error) {
	switch v := next.(type) {
	case int:
		return fmt.Sprintf("%s[%d]", prev, v), nil
	case string:
		if keyMustBeQuoted(v) {
			return fmt.Sprintf("%s[%s]", prev, escape(v)), nil
		} else {
			return fmt.Sprintf("%s.%s", prev, v), nil
		}
	default:
		return "", fmt.Errorf("could not form prefix for %#v", next)
	}
}

type statementGroup struct {
	statements []string
}

func (s *statementGroup) Add(prefix, value string) {
	s.statements = append(s.statements, fmt.Sprintf("%s = %s;", prefix, value))
}

func (s *statementGroup) AddGroup(g *statementGroup) {
	s.statements = append(s.statements, g.statements...)
}

func makeStatements(prefix string, v interface{}) (*statementGroup, error) {
	ss := &statementGroup{make([]string, 0)}

	switch vv := v.(type) {

	case map[string]interface{}:
		// It's an object
		ss.Add(prefix, "{}")

		for k, sub := range vv {
			newPrefix, err := makePrefix(prefix, k)
			if err != nil {
				return ss, err
			}
			extra, err := makeStatements(newPrefix, sub)
			if err != nil {
				return ss, err
			}
			ss.AddGroup(extra)
		}

	case []interface{}:
		// It's an array
		ss.Add(prefix, "[]")

		for k, sub := range vv {
			newPrefix, err := makePrefix(prefix, k)
			if err != nil {
				return ss, err
			}
			extra, err := makeStatements(newPrefix, sub)
			if err != nil {
				return ss, err
			}
			ss.AddGroup(extra)
		}

	case float64:
		ss.Add(prefix, escape(vv))

	case string:
		ss.Add(prefix, escape(vv))

	case bool:
		ss.Add(prefix, fmt.Sprintf("%t", vv))

	case nil:
		ss.Add(prefix, "null")
	}

	return ss, nil
}

func escape(s interface{}) string {
	// I'm pretty sure it's safe to ignore this error
	// ...maybe. I'll work something into this I promise
	out, _ := json.Marshal(s)
	return string(out)
}

func todo(err error) {
	log.Fatalf("TODO: %s", err)
}
<|endoftext|>


<commit_before>// Package main defines a command line interface for the sqlboiler package
package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/kat-co/vala"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cmdState  *State
	cmdConfig *Config
)

func main() {
	var err error

	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "./"
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Find and read config, ignore errors because we'll fall back to defaults
	// and other validation mechanisms
	_ = viper.ReadInConfig()

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates boilerplate structs and statements",
		Long: "SQL Boiler generates boilerplate structs and statements from template files.\n" +
			`Complete documentation is available at http://github.com/nullbio/sqlboiler`,
		Example:       `sqlboiler -o models -p models postgres`,
		PreRunE:       preRun,
		RunE:          run,
		PostRunE:      postRun,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringSliceP("tables", "t", nil, "Tables to generate models for, all tables if empty")
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")

	viper.SetDefault("postgres.ssl_mode", "required")
	viper.BindPFlags(rootCmd.PersistentFlags())

	if err := rootCmd.Execute(); err != nil {
		if e, ok := err.(commandFailure); ok {
			rootCmd.HelpFunc()
			fmt.Printf("\n%s\n", string(e))
			return
		}

		fmt.Printf("\n%+v\n", err)
		os.Exit(1)
	}
}

type commandFailure string

func (c commandFailure) Error() string {
	return string(c)
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return commandFailure("must provide a driver name")
	}

	cmdConfig = &Config{
		DriverName: args[0],
		OutFolder:  viper.GetString("output"),
		PkgName:    viper.GetString("pkgname"),
	}

	// BUG: https://github.com/spf13/viper/issues/200
	// Look up the value of TableNames directly from PFlags in Cobra if we
	// detect a malformed value coming out of viper.
	// Once the bug is fixed we'll be able to move this into the init above
	cmdConfig.TableNames = viper.GetStringSlice("tables")
	if len(cmdConfig.TableNames) == 1 && strings.HasPrefix(cmdConfig.TableNames[0], "[") {
		cmdConfig.TableNames, err = cmd.PersistentFlags().GetStringSlice("tables")
		if err != nil {
			return err
		}
	}

	if viper.IsSet("postgres.dbname") {
		cmdConfig.Postgres = PostgresConfig{
			User:    viper.GetString("postgres.user"),
			Pass:    viper.GetString("postgres.pass"),
			Host:    viper.GetString("postgres.host"),
			Port:    viper.GetInt("postgres.port"),
			DBName:  viper.GetString("postgres.dbname"),
			SSLMode: viper.GetString("postgres.sslmode"),
		}

		// Set the default SSLMode value
		if cmdConfig.Postgres.SSLMode == "" {
			viper.Set("postgres.sslmode", "require")
			cmdConfig.Postgres.SSLMode = viper.GetString("postgres.sslmode")
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.Postgres.User, "postgres.user"),
			vala.StringNotEmpty(cmdConfig.Postgres.Pass, "postgres.pass"),
			vala.StringNotEmpty(cmdConfig.Postgres.Host, "postgres.host"),
			vala.Not(vala.Equals(cmdConfig.Postgres.Port, 0, "postgres.port")),
			vala.StringNotEmpty(cmdConfig.Postgres.DBName, "postgres.dbname"),
			vala.StringNotEmpty(cmdConfig.Postgres.SSLMode, "postgres.sslmode"),
		).Check()

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

	cmdState, err = New(cmdConfig)
	return err
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run(true)
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}
<commit_msg>Die if postgres driver config is missing<commit_after>// Package main defines a command line interface for the sqlboiler package
package main

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/kat-co/vala"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cmdState  *State
	cmdConfig *Config
)

func main() {
	var err error

	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "./"
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Find and read config, ignore errors because we'll fall back to defaults
	// and other validation mechanisms
	_ = viper.ReadInConfig()

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates boilerplate structs and statements",
		Long: "SQL Boiler generates boilerplate structs and statements from template files.\n" +
			`Complete documentation is available at http://github.com/nullbio/sqlboiler`,
		Example:       `sqlboiler -o models -p models postgres`,
		PreRunE:       preRun,
		RunE:          run,
		PostRunE:      postRun,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringSliceP("tables", "t", nil, "Tables to generate models for, all tables if empty")
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")

	viper.SetDefault("postgres.ssl_mode", "required")
	viper.BindPFlags(rootCmd.PersistentFlags())

	if err := rootCmd.Execute(); err != nil {
		if e, ok := err.(commandFailure); ok {
			rootCmd.HelpFunc()
			fmt.Printf("\n%s\n", string(e))
			return
		}

		fmt.Printf("%+v\n", err)
		os.Exit(1)
	}
}

type commandFailure string

func (c commandFailure) Error() string {
	return string(c)
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return commandFailure("must provide a driver name")
	}

	driverName := args[0]

	cmdConfig = &Config{
		DriverName: driverName,
		OutFolder:  viper.GetString("output"),
		PkgName:    viper.GetString("pkgname"),
	}

	// BUG: https://github.com/spf13/viper/issues/200
	// Look up the value of TableNames directly from PFlags in Cobra if we
	// detect a malformed value coming out of viper.
	// Once the bug is fixed we'll be able to move this into the init above
	cmdConfig.TableNames = viper.GetStringSlice("tables")
	if len(cmdConfig.TableNames) == 1 && strings.HasPrefix(cmdConfig.TableNames[0], "[") {
		cmdConfig.TableNames, err = cmd.PersistentFlags().GetStringSlice("tables")
		if err != nil {
			return err
		}
	}

	if viper.IsSet("postgres.dbname") {
		cmdConfig.Postgres = PostgresConfig{
			User:    viper.GetString("postgres.user"),
			Pass:    viper.GetString("postgres.pass"),
			Host:    viper.GetString("postgres.host"),
			Port:    viper.GetInt("postgres.port"),
			DBName:  viper.GetString("postgres.dbname"),
			SSLMode: viper.GetString("postgres.sslmode"),
		}

		// Set the default SSLMode value
		if cmdConfig.Postgres.SSLMode == "" {
			viper.Set("postgres.sslmode", "require")
			cmdConfig.Postgres.SSLMode = viper.GetString("postgres.sslmode")
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.Postgres.User, "postgres.user"),
			vala.StringNotEmpty(cmdConfig.Postgres.Pass, "postgres.pass"),
			vala.StringNotEmpty(cmdConfig.Postgres.Host, "postgres.host"),
			vala.Not(vala.Equals(cmdConfig.Postgres.Port, 0, "postgres.port")),
			vala.StringNotEmpty(cmdConfig.Postgres.DBName, "postgres.dbname"),
			vala.StringNotEmpty(cmdConfig.Postgres.SSLMode, "postgres.sslmode"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	} else if driverName == "postgres" {
		return errors.New("postgres driver requires a postgres section in the config")
	}

	cmdState, err = New(cmdConfig)
	return err
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run(true)
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"syscall"
)

var (
	Ku        *Kurafuto
	verbosity int
)

func sigintQuit(c <-chan os.Signal) {
	<-c
	if Ku == nil {
		return
	}
	log.Println("Shutting down!")
	Ku.Quit()
}

func sighupReload(c <-chan os.Signal) {
	for {
		<-c
		if Ku == nil {
			return
		}
		log.Println("Should reload config now, but that isn't implemented.")
	}
}

////////////////////

func main() {
	configFile := flag.String("config", "kurafuto.json", "the file your Kurafuto configuration is stored in.")
	//forceSalt := flag.String("forceSalt", "", "force a specific salt to be used (don't do this!)")
	flag.IntVar(&verbosity, "v", 0, "Debugging verbosity level.")
	flag.Parse()

	config, err := NewConfigFile(*configFile)
	if err != nil {
		log.Fatal(err)
	}

	ku, err := NewKurafuto(config)
	if err != nil {
		log.Fatal(err)
	}

	/*if *forceSalt != "" {
		ku.salt = *forceSalt
	}*/

	Ku = ku // Make it global.

	Infof("Kurafuto now listening on %s:%d with %d servers", config.Address, config.Port, len(config.Servers))
	Debugf("Debugging level %d enabled! (Salt: %s)", verbosity, Ku.salt)
	if len(config.Ignore) > 0 {
		Debugf("Ignoring these packets: %s", config.Ignore.String())
	}
	if len(config.Drop) > 0 {
		Debugf("Dropping these packets: %s", config.Drop.String())
	}
	if len(config.DropExts) > 0 {
		Debugf("Dropping these extensions: %s", config.DropExts)
	}

	sigint := make(chan os.Signal, 1)
	signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
	go sigintQuit(sigint)

	sighup := make(chan os.Signal, 3)
	signal.Notify(sighup, syscall.SIGHUP)
	go sighupReload(sighup)

	go ku.Run()
	<-ku.Done
}
<commit_msg>Enable multi-core usage.<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"syscall"
	"runtime"
)

var (
	Ku        *Kurafuto
	verbosity int
)

func sigintQuit(c <-chan os.Signal) {
	<-c
	if Ku == nil {
		return
	}
	log.Println("Shutting down!")
	Ku.Quit()
}

func sighupReload(c <-chan os.Signal) {
	for {
		<-c
		if Ku == nil {
			return
		}
		log.Println("Should reload config now, but that isn't implemented.")
	}
}

////////////////////
//Detect number of CPU cores to use
var cpus = runtime.NumCPU()

func main() {
	//Enable Multiple core usage
	runtime.GOMAXPROCS(cpus)
	configFile := flag.String("config", "kurafuto.json", "the file your Kurafuto configuration is stored in.")
	//forceSalt := flag.String("forceSalt", "", "force a specific salt to be used (don't do this!)")
	flag.IntVar(&verbosity, "v", 0, "Debugging verbosity level.")
	flag.Parse()

	config, err := NewConfigFile(*configFile)
	if err != nil {
		log.Fatal(err)
	}

	ku, err := NewKurafuto(config)
	if err != nil {
		log.Fatal(err)
	}

	/*if *forceSalt != "" {
		ku.salt = *forceSalt
	}*/

	Ku = ku // Make it global.

	Infof("Kurafuto now listening on %s:%d with %d servers", config.Address, config.Port, len(config.Servers))
	Debugf("Debugging level %d enabled! (Salt: %s)", verbosity, Ku.salt)
	if len(config.Ignore) > 0 {
		Debugf("Ignoring these packets: %s", config.Ignore.String())
	}
	if len(config.Drop) > 0 {
		Debugf("Dropping these packets: %s", config.Drop.String())
	}
	if len(config.DropExts) > 0 {
		Debugf("Dropping these extensions: %s", config.DropExts)
	}

	sigint := make(chan os.Signal, 1)
	signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
	go sigintQuit(sigint)

	sighup := make(chan os.Signal, 3)
	signal.Notify(sighup, syscall.SIGHUP)
	go sighupReload(sighup)

	go ku.Run()
	<-ku.Done
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-redis/redis"
	"net/http"
	"html/template"
	"strconv"
	"time"
	"fmt"
	"log"
)

// Change these settings as needed
var client = redis.NewClient(&redis.Options{
          Addr:     "localhost:6379",
          Password: "",
          DB:       0,
})

// Load template into RAM; only one request to logs.html is necessary per server
var logTemplate = template.Must(template.ParseFiles("logs.html"))

// Main handler; displays web page.
func logsHandler(w http.ResponseWriter, r *http.Request) {
	// Execute values into template
	err := logTemplate.Execute(w, nil)
	// If the server can't access the template, bail
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// I don't like that there are four handlers doing essentially the same thing;
// Could at least use just 1 function.
// Clean-up later.
func nuiHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("nui").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func forestHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("forest").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func aboveHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("atc").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func belowHandler(w http.ResponseWriter, r *http.Request) {
	str, _ := client.Get("btc").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func countDown(key string) {
	// Get the value of key
	str, err := client.Get(key).Result()
	if err != nil {
		fmt.Println(err)
	}
	// Convert key's value to an integer, so it can be
	// decremented and compared to other ints.
	val, err := strconv.Atoi(str)
	// If an error occurs (if the string is not a number),
	// return. Should never happen, since the value has
	// already been parsed by the save function
	if err != nil {
		return
	}
	for ; val >= 0; val-- {
		// Save the value to the database every interval of 5
		// Can cause some weird behaviour, but saves insignificant
		// amounts of stress on server :^)
		if val % 5 == 0 || val == 0 {
			// Error value is ignored for now.
			client.Set(key, val, 0).Err()
		}
		// Slightly inaccurate if I remember correctly
		// For our purposes, should work fine
		time.Sleep(time.Second)
	}
	return
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	// Initializes r to be parsed
	r.ParseForm()
	var key string
	// There has to be a better way
	for key, _ = range r.Form {
		;
	}
	val := r.FormValue(key)
	newVal, err := strconv.Atoi(val)
	// If the value is not able to be converted to an int
	// (if it's not a number)
	// return.
	// Error is already handed client side; anyone entering
	// invalid input is bypassing said error
	if err != nil {
		return
	}
	// If no value is entered
	// Should also be illegal through normal methods
	if newVal == 0 {
		// Redirect back to main page
		http.Redirect(w, r, "/logs/", http.StatusFound)
		return
	}
	// Save the value in the database, converting minutes to seconds
	client.Set(key, (newVal*60), 0).Err()
	// Start countdown for newly saved value
	go countDown(key)
	http.Redirect(w, r, "/logs/", http.StatusFound)
}

func main() {
	http.HandleFunc("/logs/", logsHandler)
	http.HandleFunc("/save/", saveHandler)
	http.HandleFunc("/nuitimer/", nuiHandler)
	http.HandleFunc("/foresttimer/", forestHandler)
	http.HandleFunc("/abovetimer/", aboveHandler)
	http.HandleFunc("/belowtimer/", belowHandler)
	// FileServer handler
	http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./img/"))))
	// Starts the countdown for all four functions on server start
	// these will self-terminate if unnecessary
	go countDown("nui")
	go countDown("forest")
	go countDown("atc")
	go countDown("btc")
	// Start listening on port 80 (default port for http), logging
	// Fatal errors (and closing upon error)
	log.Fatal(http.ListenAndServe(":80", nil))
}
<commit_msg>Fix err being null<commit_after>package main

import (
	"github.com/go-redis/redis"
	"net/http"
	"html/template"
	"strconv"
	"time"
	"fmt"
	"log"
)

// Change these settings as needed
var client = redis.NewClient(&redis.Options{
          Addr:     "localhost:6379",
          Password: "",
          DB:       0,
})

// Load template into RAM; only one request to logs.html is necessary per server
var logTemplate = template.Must(template.ParseFiles("logs.html"))

// Main handler; displays web page.
func logsHandler(w http.ResponseWriter, r *http.Request) {
	// Execute values into template
	err := logTemplate.Execute(w, nil)
	// If the server can't access the template, bail
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// I don't like that there are four handlers doing essentially the same thing;
// Could at least use just 1 function.
// Clean-up later.
func nuiHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("nui").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func forestHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("forest").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func aboveHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("atc").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func belowHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("btc").Result()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprint(w, str)
}

func countDown(key string) {
	// Get the value of key
	str, err := client.Get(key).Result()
	if err != nil {
		fmt.Println(err)
	}
	// Convert key's value to an integer, so it can be
	// decremented and compared to other ints.
	val, err := strconv.Atoi(str)
	// If an error occurs (if the string is not a number),
	// return. Should never happen, since the value has
	// already been parsed by the save function
	if err != nil {
		return
	}
	for ; val >= 0; val-- {
		// Save the value to the database every interval of 5
		// Can cause some weird behaviour, but saves insignificant
		// amounts of stress on server :^)
		if val % 5 == 0 || val == 0 {
			// Error value is ignored for now.
			client.Set(key, val, 0).Err()
		}
		// Slightly inaccurate if I remember correctly
		// For our purposes, should work fine
		time.Sleep(time.Second)
	}
	return
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	// Initializes r to be parsed
	r.ParseForm()
	var key string
	// There has to be a better way
	for key, _ = range r.Form {
		;
	}
	val := r.FormValue(key)
	newVal, err := strconv.Atoi(val)
	// If the value is not able to be converted to an int
	// (if it's not a number)
	// return.
	// Error is already handed client side; anyone entering
	// invalid input is bypassing said error
	if err != nil {
		return
	}
	// If no value is entered
	// Should also be illegal through normal methods
	if newVal == 0 {
		// Redirect back to main page
		http.Redirect(w, r, "/logs/", http.StatusFound)
		return
	}
	// Save the value in the database, converting minutes to seconds
	client.Set(key, (newVal*60), 0).Err()
	// Start countdown for newly saved value
	go countDown(key)
	http.Redirect(w, r, "/logs/", http.StatusFound)
}

func main() {
	http.HandleFunc("/logs/", logsHandler)
	http.HandleFunc("/save/", saveHandler)
	http.HandleFunc("/nuitimer/", nuiHandler)
	http.HandleFunc("/foresttimer/", forestHandler)
	http.HandleFunc("/abovetimer/", aboveHandler)
	http.HandleFunc("/belowtimer/", belowHandler)
	// FileServer handler
	http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./img/"))))
	// Starts the countdown for all four functions on server start
	// these will self-terminate if unnecessary
	go countDown("nui")
	go countDown("forest")
	go countDown("atc")
	go countDown("btc")
	// Start listening on port 80 (default port for http), logging
	// Fatal errors (and closing upon error)
	log.Fatal(http.ListenAndServe(":80", nil))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/RedisLabs/RediSearchBenchmark/index"
	"github.com/RedisLabs/RediSearchBenchmark/index/elastic"
	"github.com/RedisLabs/RediSearchBenchmark/index/redisearch"
	"github.com/RedisLabs/RediSearchBenchmark/index/solr"
	"github.com/RedisLabs/RediSearchBenchmark/ingest"
	"github.com/RedisLabs/RediSearchBenchmark/query"
)

// IndexName is the name of our index on all engines
const IndexName = "wik"

var indexMetadata = index.NewMetadata().
	AddField(index.NewTextField("title", 10)).
	AddField(index.NewTextField("body", 1))

// selectIndex selects and configures the index we are now running based on the engine name, hosts and number of shards
func selectIndex(engine string, hosts []string, partitions int) (index.Index, index.Autocompleter, interface{}) {

	switch engine {
	case "redis":
		//return redisearch.NewIndex(hosts[0], "wik{0}", indexMetadata)
		idx := redisearch.NewDistributedIndex(IndexName, hosts, partitions, indexMetadata)
		return idx, idx, query.QueryVerbatim
	case "elastic":
		idx, err := elastic.NewIndex(hosts[0], IndexName, indexMetadata)
		if err != nil {
			panic(err)
		}
		return idx, idx, 0
	case "solr":
		idx, err := solr.NewIndex(hosts[0], IndexName, indexMetadata)
		if err != nil {
			panic(err)
		}
		return idx, idx, 0

	}
	panic("could not find index type " + engine)
}

func main() {

	hosts := flag.String("hosts", "localhost:6379", "comma separated list of host:port to redis nodes")
	partitions := flag.Int("shards", 1, "the number of partitions we want (AT LEAST the number of cluster shards)")
	fileName := flag.String("file", "", "Input file to ingest data from (wikipedia abstracts)")
	scoreFile := flag.String("scores", "", "read scores of documents CSV for indexing")
	engine := flag.String("engine", "redis", "The search backend to run")
	benchmark := flag.String("benchmark", "", "[search|suggest] - if set, we run the given benchmark")
	fuzzy := flag.Bool("fuzzy", false, "For redis only - benchmark fuzzy auto suggest")
	seconds := flag.Int("duration", 5, "number of seconds to run the benchmark")
	conc := flag.Int("c", 4, "benchmark concurrency")
	qs := flag.String("queries", "hello world", "comma separated list of queries to benchmark")
	outfile := flag.String("o", "benchmark.csv", "results output file. set to - for stdout")
	duration := time.Second * time.Duration(*seconds)

	flag.Parse()
	servers := strings.Split(*hosts, ",")
	if len(servers) == 0 {
		panic("No servers given")
	}
	queries := strings.Split(*qs, ",")

	// select index to run
	idx, ac, opts := selectIndex(*engine, servers, *partitions)

	// Search benchmark
	if *benchmark == "search" {
		name := fmt.Sprintf("search: %s", *qs)
		Benchmark(*conc, duration, *engine, name, *outfile, SearchBenchmark(queries, idx, opts))
		os.Exit(0)
	}

	// Auto-suggest benchmark
	if *benchmark == "suggest" {
		Benchmark(*conc, duration, *engine, "suggest", *outfile, AutocompleteBenchmark(ac, *fuzzy))
		os.Exit(0)
	}

	// ingest documents into the selected engine
	if *fileName != "" && *benchmark == "" {
		if ac != nil {
			ac.Delete()
		}

		idx.Drop()
		idx.Create()
		wr := ingest.NewWikipediaAbstractsReader()

		if *scoreFile != "" {
			if err := wr.LoadScores(*scoreFile); err != nil {
				panic(err)
			}
		}

		if err := ingest.IngestDocuments(*fileName, wr, idx, ac, nil, 50000); err != nil {
			panic(err)
		}

		os.Exit(0)
	}

	fmt.Fprintln(os.Stderr, "No benchmark or input file specified")
	flag.Usage()
	os.Exit(-1)
}
<commit_msg>fixed elastic init<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/RedisLabs/RediSearchBenchmark/index"
	"github.com/RedisLabs/RediSearchBenchmark/index/elastic"
	"github.com/RedisLabs/RediSearchBenchmark/index/redisearch"
	"github.com/RedisLabs/RediSearchBenchmark/index/solr"
	"github.com/RedisLabs/RediSearchBenchmark/ingest"
	"github.com/RedisLabs/RediSearchBenchmark/query"
)

// IndexName is the name of our index on all engines
const IndexName = "wik"

var indexMetadata = index.NewMetadata().
	AddField(index.NewTextField("title", 10)).
	AddField(index.NewTextField("body", 1))

// selectIndex selects and configures the index we are now running based on the engine name, hosts and number of shards
func selectIndex(engine string, hosts []string, partitions int) (index.Index, index.Autocompleter, interface{}) {

	switch engine {
	case "redis":
		//return redisearch.NewIndex(hosts[0], "wik{0}", indexMetadata)
		idx := redisearch.NewDistributedIndex(IndexName, hosts, partitions, indexMetadata)
		return idx, idx, query.QueryVerbatim
	case "elastic":
		idx, err := elastic.NewIndex(hosts[0], IndexName, "doc", indexMetadata)
		if err != nil {
			panic(err)
		}
		return idx, idx, 0
	case "solr":
		idx, err := solr.NewIndex(hosts[0], IndexName, indexMetadata)
		if err != nil {
			panic(err)
		}
		return idx, idx, 0

	}
	panic("could not find index type " + engine)
}

func main() {

	hosts := flag.String("hosts", "localhost:6379", "comma separated list of host:port to redis nodes")
	partitions := flag.Int("shards", 1, "the number of partitions we want (AT LEAST the number of cluster shards)")
	fileName := flag.String("file", "", "Input file to ingest data from (wikipedia abstracts)")
	scoreFile := flag.String("scores", "", "read scores of documents CSV for indexing")
	engine := flag.String("engine", "redis", "The search backend to run")
	benchmark := flag.String("benchmark", "", "[search|suggest] - if set, we run the given benchmark")
	fuzzy := flag.Bool("fuzzy", false, "For redis only - benchmark fuzzy auto suggest")
	seconds := flag.Int("duration", 5, "number of seconds to run the benchmark")
	conc := flag.Int("c", 4, "benchmark concurrency")
	qs := flag.String("queries", "hello world", "comma separated list of queries to benchmark")
	outfile := flag.String("o", "benchmark.csv", "results output file. set to - for stdout")
	duration := time.Second * time.Duration(*seconds)

	flag.Parse()
	servers := strings.Split(*hosts, ",")
	if len(servers) == 0 {
		panic("No servers given")
	}
	queries := strings.Split(*qs, ",")

	// select index to run
	idx, ac, opts := selectIndex(*engine, servers, *partitions)

	// Search benchmark
	if *benchmark == "search" {
		name := fmt.Sprintf("search: %s", *qs)
		Benchmark(*conc, duration, *engine, name, *outfile, SearchBenchmark(queries, idx, opts))
		os.Exit(0)
	}

	// Auto-suggest benchmark
	if *benchmark == "suggest" {
		Benchmark(*conc, duration, *engine, "suggest", *outfile, AutocompleteBenchmark(ac, *fuzzy))
		os.Exit(0)
	}

	// ingest documents into the selected engine
	if *fileName != "" && *benchmark == "" {
		if ac != nil {
			ac.Delete()
		}

		idx.Drop()
		idx.Create()
		wr := ingest.NewWikipediaAbstractsReader()

		if *scoreFile != "" {
			if err := wr.LoadScores(*scoreFile); err != nil {
				panic(err)
			}
		}

		if err := ingest.IngestDocuments(*fileName, wr, idx, ac, nil, 50000); err != nil {
			panic(err)
		}

		os.Exit(0)
	}

	fmt.Fprintln(os.Stderr, "No benchmark or input file specified")
	flag.Usage()
	os.Exit(-1)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"sphere2cubeGo/cache"
	"time"
	"flag"
	"sphere2cubeGo/worker"
	"sphere2cubeGo/saver"
)

var (
	tileNames = []string{
		worker.TileUp,
		worker.TileDown,
		worker.TileFront,
		worker.TileRight,
		worker.TileBack,
		worker.TileLeft,
	}
	tileSize          = 1024
	originalImagePath = ""
	outPutDir         = "result"
)

func main() {

	tileSizeCmd := flag.Int("tile_size", tileSize, "Size in px of final tile")
	originalImagePathCmd := flag.String("input_path", "", "")
	outPutDirCmd := flag.String("output_path", outPutDir, "")

	flag.Parse()

	tileSize = *tileSizeCmd
	originalImagePath = *originalImagePathCmd
	outPutDir = *outPutDirCmd

	if originalImagePath == "" {
		log.Fatal("input_path is required")
		os.Exit(1)
	}

	_, err := os.Stat(originalImagePath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatalf("%v not found", originalImagePath)
		}
	}

	done := make(chan worker.TileResult)
	timeStart := time.Now()
	cacheResult := cache.CacheAnglesHandler(tileSize)
	for _, tileName := range tileNames {
		tile := worker.Tile{TileName: tileName, TileSize: tileSize}
		go worker.Worker(tile, cacheResult, originalImagePath, done)
	}

	for range tileNames {
		tileResult := <-done
		err = saver.SaveTile(tileResult, outPutDir)

		if err != nil {
			panic(err)
		}

		log.Printf("Process for tile %v --> finished", tileResult.Tile.TileName)
	}

	timeFinish := time.Now()
	duration := timeFinish.Sub(timeStart)
	log.Printf("Time to render: %v seconds", duration.Seconds())
}
<commit_msg>improve help messages<commit_after>package main

import (
	"log"
	"os"
	"sphere2cubeGo/cache"
	"time"
	"flag"
	"sphere2cubeGo/worker"
	"sphere2cubeGo/saver"
)

var (
	tileNames = []string{
		worker.TileUp,
		worker.TileDown,
		worker.TileFront,
		worker.TileRight,
		worker.TileBack,
		worker.TileLeft,
	}
	tileSize          = 1024
	originalImagePath = ""
	outPutDir         = "./build"
)

func main() {

	tileSizeCmd := flag.Int("s", tileSize, "Size in px of final tile")
	originalImagePathCmd := flag.String("i", "", "Path to input equirectangular panorama")
	outPutDirCmd := flag.String("o", outPutDir, "Path to output directory")

	flag.Parse()

	tileSize = *tileSizeCmd
	originalImagePath = *originalImagePathCmd
	outPutDir = *outPutDirCmd

	if originalImagePath == "" {
		flag.PrintDefaults()
		os.Exit(2)
	}

	_, err := os.Stat(originalImagePath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatalf("%v not found", originalImagePath)
		}
	}

	done := make(chan worker.TileResult)
	timeStart := time.Now()
	cacheResult := cache.CacheAnglesHandler(tileSize)
	for _, tileName := range tileNames {
		tile := worker.Tile{TileName: tileName, TileSize: tileSize}
		go worker.Worker(tile, cacheResult, originalImagePath, done)
	}

	for range tileNames {
		tileResult := <-done
		err = saver.SaveTile(tileResult, outPutDir)

		if err != nil {
			panic(err)
		}

		log.Printf("Process for tile %v --> finished", tileResult.Tile.TileName)
	}

	timeFinish := time.Now()
	duration := timeFinish.Sub(timeStart)
	log.Printf("Time to render: %v seconds", duration.Seconds())
}
<|endoftext|>


<commit_before>package main

import (
	"strings"

	ccli "github.com/micro/cli"
	"github.com/micro/go-micro/cmd"
	"github.com/micro/micro/api"
	"github.com/micro/micro/bot"
	"github.com/micro/micro/car"
	"github.com/micro/micro/cli"
	"github.com/micro/micro/new"
	"github.com/micro/micro/plugin"
	"github.com/micro/micro/run"
	"github.com/micro/micro/web"
)

func setup(app *ccli.App) {
	app.Flags = append(app.Flags,
		ccli.BoolFlag{
			Name:   "enable_tls",
			Usage:  "Enable TLS",
			EnvVar: "MICRO_ENABLE_TLS",
		},
		ccli.StringFlag{
			Name:   "tls_cert_file",
			Usage:  "TLS Certificate file",
			EnvVar: "MICRO_TLS_CERT_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_key_file",
			Usage:  "TLS Key file",
			EnvVar: "MICRO_TLS_KEY_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_client_ca_file",
			Usage:  "TLS CA file to verify clients against",
			EnvVar: "MICRO_TLS_CLIENT_CA_FILE",
		},
		ccli.StringFlag{
			Name:   "api_address",
			Usage:  "Set the api address e.g 0.0.0.0:8080",
			EnvVar: "MICRO_API_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "proxy_address",
			Usage:  "Proxy requests via the HTTP address specified",
			EnvVar: "MICRO_PROXY_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "sidecar_address",
			Usage:  "Set the sidecar address e.g 0.0.0.0:8081",
			EnvVar: "MICRO_SIDECAR_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "web_address",
			Usage:  "Set the web UI address e.g 0.0.0.0:8082",
			EnvVar: "MICRO_WEB_ADDRESS",
		},
		ccli.IntFlag{
			Name:   "register_ttl",
			EnvVar: "MICRO_REGISTER_TTL",
			Usage:  "Register TTL in seconds",
		},
		ccli.IntFlag{
			Name:   "register_interval",
			EnvVar: "MICRO_REGISTER_INTERVAL",
			Usage:  "Register interval in seconds",
		},
		ccli.StringFlag{
			Name:   "api_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {api, proxy, rpc}",
			EnvVar: "MICRO_API_HANDLER",
		},
		ccli.StringFlag{
			Name:   "api_namespace",
			Usage:  "Set the namespace used by the API e.g. com.example.api",
			EnvVar: "MICRO_API_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "sidecar_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {proxy, rpc}",
			EnvVar: "MICRO_SIDECAR_HANDLER",
		},
		ccli.StringFlag{
			Name:   "sidecar_namespace",
			Usage:  "Set the namespace used by the Sidecar e.g. com.example.srv",
			EnvVar: "MICRO_SIDECAR_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "web_namespace",
			Usage:  "Set the namespace used by the Web proxy e.g. com.example.web",
			EnvVar: "MICRO_WEB_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "api_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_API_CORS",
		},
		ccli.StringFlag{
			Name:   "web_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_WEB_CORS",
		},
		ccli.StringFlag{
			Name:   "sidecar_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_SIDECAR_CORS",
		},
		ccli.BoolFlag{
			Name:   "enable_stats",
			Usage:  "Enable stats",
			EnvVar: "MICRO_ENABLE_STATS",
		},
	)

	plugins := plugin.Plugins()

	for _, p := range plugins {
		if flags := p.Flags(); len(flags) > 0 {
			app.Flags = append(app.Flags, flags...)
		}

		if cmds := p.Commands(); len(cmds) > 0 {
			app.Commands = append(app.Commands, cmds...)
		}
	}

	before := app.Before

	app.Before = func(ctx *ccli.Context) error {
		if len(ctx.String("api_handler")) > 0 {
			api.Handler = ctx.String("api_handler")
		}
		if len(ctx.String("api_address")) > 0 {
			api.Address = ctx.String("api_address")
		}
		if len(ctx.String("sidecar_address")) > 0 {
			car.Address = ctx.String("sidecar_address")
		}
		if len(ctx.String("web_address")) > 0 {
			web.Address = ctx.String("web_address")
		}
		if len(ctx.String("api_namespace")) > 0 {
			api.Namespace = ctx.String("api_namespace")
		}
		if len(ctx.String("sidecar_handler")) > 0 {
			car.Handler = ctx.String("sidecar_handler")
		}
		if len(ctx.String("sidecar_namespace")) > 0 {
			car.Namespace = ctx.String("sidecar_namespace")
		}
		if len(ctx.String("web_namespace")) > 0 {
			web.Namespace = ctx.String("web_namespace")
		}

		// origin comma separated string to map
		fn := func(s string) map[string]bool {
			origins := make(map[string]bool)
			for _, origin := range strings.Split(s, ",") {
				origins[origin] = true
			}
			return origins
		}

		if len(ctx.String("api_cors")) > 0 {
			api.CORS = fn(ctx.String("api_cors"))
		}
		if len(ctx.String("sidecar_cors")) > 0 {
			car.CORS = fn(ctx.String("sidecar_cors"))
		}
		if len(ctx.String("web_cors")) > 0 {
			web.CORS = fn(ctx.String("web_cors"))
		}

		for _, p := range plugins {
			p.Init(ctx)
		}

		return before(ctx)
	}
}

func main() {
	app := cmd.App()
	app.Commands = append(app.Commands, api.Commands()...)
	app.Commands = append(app.Commands, bot.Commands()...)
	app.Commands = append(app.Commands, cli.Commands()...)
	app.Commands = append(app.Commands, car.Commands()...)
	app.Commands = append(app.Commands, new.Commands()...)
	app.Commands = append(app.Commands, run.Commands()...)
	app.Commands = append(app.Commands, web.Commands()...)
	app.Action = func(context *ccli.Context) { ccli.ShowAppHelp(context) }

	setup(app)

	cmd.Init(
		cmd.Name("micro"),
		cmd.Description("A microservices toolkit"),
		cmd.Version("latest"),
	)
}
<commit_msg>add version<commit_after>package main

import (
	"strings"

	ccli "github.com/micro/cli"
	"github.com/micro/go-micro/cmd"
	"github.com/micro/micro/api"
	"github.com/micro/micro/bot"
	"github.com/micro/micro/car"
	"github.com/micro/micro/cli"
	"github.com/micro/micro/new"
	"github.com/micro/micro/plugin"
	"github.com/micro/micro/run"
	"github.com/micro/micro/web"
)

var (
	name = "micro"
	description = "A microservices toolkit"
	version = "0.1.0"
)

func setup(app *ccli.App) {
	app.Flags = append(app.Flags,
		ccli.BoolFlag{
			Name:   "enable_tls",
			Usage:  "Enable TLS",
			EnvVar: "MICRO_ENABLE_TLS",
		},
		ccli.StringFlag{
			Name:   "tls_cert_file",
			Usage:  "TLS Certificate file",
			EnvVar: "MICRO_TLS_CERT_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_key_file",
			Usage:  "TLS Key file",
			EnvVar: "MICRO_TLS_KEY_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_client_ca_file",
			Usage:  "TLS CA file to verify clients against",
			EnvVar: "MICRO_TLS_CLIENT_CA_FILE",
		},
		ccli.StringFlag{
			Name:   "api_address",
			Usage:  "Set the api address e.g 0.0.0.0:8080",
			EnvVar: "MICRO_API_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "proxy_address",
			Usage:  "Proxy requests via the HTTP address specified",
			EnvVar: "MICRO_PROXY_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "sidecar_address",
			Usage:  "Set the sidecar address e.g 0.0.0.0:8081",
			EnvVar: "MICRO_SIDECAR_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "web_address",
			Usage:  "Set the web UI address e.g 0.0.0.0:8082",
			EnvVar: "MICRO_WEB_ADDRESS",
		},
		ccli.IntFlag{
			Name:   "register_ttl",
			EnvVar: "MICRO_REGISTER_TTL",
			Usage:  "Register TTL in seconds",
		},
		ccli.IntFlag{
			Name:   "register_interval",
			EnvVar: "MICRO_REGISTER_INTERVAL",
			Usage:  "Register interval in seconds",
		},
		ccli.StringFlag{
			Name:   "api_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {api, proxy, rpc}",
			EnvVar: "MICRO_API_HANDLER",
		},
		ccli.StringFlag{
			Name:   "api_namespace",
			Usage:  "Set the namespace used by the API e.g. com.example.api",
			EnvVar: "MICRO_API_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "sidecar_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {proxy, rpc}",
			EnvVar: "MICRO_SIDECAR_HANDLER",
		},
		ccli.StringFlag{
			Name:   "sidecar_namespace",
			Usage:  "Set the namespace used by the Sidecar e.g. com.example.srv",
			EnvVar: "MICRO_SIDECAR_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "web_namespace",
			Usage:  "Set the namespace used by the Web proxy e.g. com.example.web",
			EnvVar: "MICRO_WEB_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "api_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_API_CORS",
		},
		ccli.StringFlag{
			Name:   "web_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_WEB_CORS",
		},
		ccli.StringFlag{
			Name:   "sidecar_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_SIDECAR_CORS",
		},
		ccli.BoolFlag{
			Name:   "enable_stats",
			Usage:  "Enable stats",
			EnvVar: "MICRO_ENABLE_STATS",
		},
	)

	plugins := plugin.Plugins()

	for _, p := range plugins {
		if flags := p.Flags(); len(flags) > 0 {
			app.Flags = append(app.Flags, flags...)
		}

		if cmds := p.Commands(); len(cmds) > 0 {
			app.Commands = append(app.Commands, cmds...)
		}
	}

	before := app.Before

	app.Before = func(ctx *ccli.Context) error {
		if len(ctx.String("api_handler")) > 0 {
			api.Handler = ctx.String("api_handler")
		}
		if len(ctx.String("api_address")) > 0 {
			api.Address = ctx.String("api_address")
		}
		if len(ctx.String("sidecar_address")) > 0 {
			car.Address = ctx.String("sidecar_address")
		}
		if len(ctx.String("web_address")) > 0 {
			web.Address = ctx.String("web_address")
		}
		if len(ctx.String("api_namespace")) > 0 {
			api.Namespace = ctx.String("api_namespace")
		}
		if len(ctx.String("sidecar_handler")) > 0 {
			car.Handler = ctx.String("sidecar_handler")
		}
		if len(ctx.String("sidecar_namespace")) > 0 {
			car.Namespace = ctx.String("sidecar_namespace")
		}
		if len(ctx.String("web_namespace")) > 0 {
			web.Namespace = ctx.String("web_namespace")
		}

		// origin comma separated string to map
		fn := func(s string) map[string]bool {
			origins := make(map[string]bool)
			for _, origin := range strings.Split(s, ",") {
				origins[origin] = true
			}
			return origins
		}

		if len(ctx.String("api_cors")) > 0 {
			api.CORS = fn(ctx.String("api_cors"))
		}
		if len(ctx.String("sidecar_cors")) > 0 {
			car.CORS = fn(ctx.String("sidecar_cors"))
		}
		if len(ctx.String("web_cors")) > 0 {
			web.CORS = fn(ctx.String("web_cors"))
		}

		for _, p := range plugins {
			p.Init(ctx)
		}

		return before(ctx)
	}
}

func main() {
	app := cmd.App()
	app.Commands = append(app.Commands, api.Commands()...)
	app.Commands = append(app.Commands, bot.Commands()...)
	app.Commands = append(app.Commands, cli.Commands()...)
	app.Commands = append(app.Commands, car.Commands()...)
	app.Commands = append(app.Commands, new.Commands()...)
	app.Commands = append(app.Commands, run.Commands()...)
	app.Commands = append(app.Commands, web.Commands()...)
	app.Action = func(context *ccli.Context) { ccli.ShowAppHelp(context) }

	setup(app)

	cmd.Init(
		cmd.Name(name),
		cmd.Description(description),
		cmd.Version(version),
	)
}
<|endoftext|>


<commit_before>package main

import (
  "github.com/nlopes/slack"
  "os"
  "log"
  "fmt"
)

func main() {
  SLACK_TOKEN := os.Getenv("SLACK_TOKEN")
  slack_api := slack.New(SLACK_TOKEN)

  boombox := boomBox{}

  go boombox.start(slack_api)

  logger := log.New(
    os.Stdout,
    "slack-bot: ",
    log.Lshortfile|log.LstdFlags,
  )

  slack.SetLogger(logger)

  rtm := slack_api.NewRTM()
  go rtm.ManageConnection()

  for msg := range rtm.IncomingEvents {
    switch ev := msg.Data.(type) {
    case *slack.MessageEvent:
      if regexp_nowplaying.MatchString(ev.Text) {
        casa_NowPlaying(slack_api, ev.Channel)
      }

      if regexp_playsong.MatchString(ev.Text) {
        casa_PlaySong(slack_api, ev)
      }

      if regexp_playeraction.MatchString(ev.Text) {
        casa_PlayerAction(slack_api, ev)
      }

      if regexp_boombox.MatchString(ev.Text) {
        user_input := regexp_boombox.FindStringSubmatch(ev.Text)[1]
        switch user_input {
        case "start":
          log.Printf("Starting BoomBox in channel [%s]", ev.Channel)
          log.Println(boombox.addChannel(ev.Channel))
        case "stop":
          log.Printf("Stopping BoomBox in channel [%s]", ev.Channel)
          log.Println(boombox.removeChannel(ev.Channel))
        }
      }
    case *slack.RTMError:
			fmt.Printf("Error: %s\n", ev.Error())

    case *slack.InvalidAuthEvent:
			log.Fatal("Invalid credentials")

    case *slack.ConnectionErrorEvent:
      log.Fatal("Failed to authenticate with Slack API")

    case *slack.ConnectingEvent:
      log.Printf("Connecting to Slack (attempt %d)", ev.Attempt)

    case *slack.HelloEvent:
      log.Printf("Received hello from Slack API.")

    case *slack.ConnectedEvent:
      log.Printf("Connected to %s as %s.", ev.Info.Team.Name, ev.Info.User.Name)

    case *slack.LatencyReport:
      log.Printf("Latency report: %s", ev.Value)

		default:
			// Ignore other events..
			// fmt.Printf("Unexpected: %v\n", msg.Data)
		}
	}
}
<commit_msg>Added error handling in main.go<commit_after>package main

import (
  "github.com/nlopes/slack"
  "os"
  "log"
  "fmt"
)

func main() {
  SLACK_TOKEN := os.Getenv("SLACK_TOKEN")
  slack_api := slack.New(SLACK_TOKEN)

  slack_message_parameters := slack.NewPostMessageParameters()
  slack_message_parameters.AsUser = true

  boombox := boomBox{}

  go boombox.start(slack_api)

  logger := log.New(
    os.Stdout,
    "slack-bot: ",
    log.Lshortfile|log.LstdFlags,
  )

  slack.SetLogger(logger)

  rtm := slack_api.NewRTM()
  go rtm.ManageConnection()

  for msg := range rtm.IncomingEvents {
    switch ev := msg.Data.(type) {
    case *slack.MessageEvent:
      if regexp_nowplaying.MatchString(ev.Text) {
        casa_NowPlaying(slack_api, ev.Channel)
      }

      if regexp_playsong.MatchString(ev.Text) {
        casa_PlaySong(slack_api, ev)
      }

      if regexp_playeraction.MatchString(ev.Text) {
        casa_PlayerAction(slack_api, ev)
      }

      if regexp_boombox.MatchString(ev.Text) {
        user_input := regexp_boombox.FindStringSubmatch(ev.Text)[1]
        switch user_input {
        case "start":
          err := boombox.addChannel(ev.Channel)
          if err != nil {
            log.Printf(
              "Failed to start BoomBox in channel [%s]: %s",
              ev.Channel,
              err,
            )
            slack_api.PostMessage(
              ev.Channel,
              fmt.Sprint(err),
              slack_message_parameters,
            )
          } else {
            log.Printf("BoomBox started in channel [%s]", ev.Channel)
            slack_api.PostMessage(
              ev.Channel,
              "BoomBox started! I'll report future song changes to this channel.",
              slack_message_parameters,
            )
          }
        case "stop":
          err := boombox.removeChannel(ev.Channel)
          if err != nil {
            log.Printf(
              "Failed to stop BoomBox in channel [%s]: %s",
              ev.Channel,
              err,
            )
            slack_api.PostMessage(
              ev.Channel,
              fmt.Sprint(err),
              slack_message_parameters,
            )
          } else {
            log.Printf("BoomBox stopped in channel [%s]", ev.Channel)
            slack_api.PostMessage(
              ev.Channel,
              "BoomBox stopped! I'll stop reporting song changes to this channel.",
              slack_message_parameters,
            )
          }
        }
      }
    case *slack.RTMError:
			fmt.Printf("Error: %s\n", ev.Error())

    case *slack.InvalidAuthEvent:
			log.Fatal("Invalid credentials")

    case *slack.ConnectionErrorEvent:
      log.Fatal("Failed to authenticate with Slack API")

    case *slack.ConnectingEvent:
      log.Printf("Connecting to Slack (attempt %d)", ev.Attempt)

    case *slack.HelloEvent:
      log.Printf("Received hello from Slack API.")

    case *slack.ConnectedEvent:
      log.Printf("Connected to %s as %s.", ev.Info.Team.Name, ev.Info.User.Name)

    case *slack.LatencyReport:
      log.Printf("Latency report: %s", ev.Value)

		default:
			// Ignore other events..
			// fmt.Printf("Unexpected: %v\n", msg.Data)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/sha1"
	"encoding/hex"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"sync"

	"code.google.com/p/go.net/idna"
	"github.com/domainr/go-whois/whois"
	_ "github.com/domainr/go-whois/servers"
)

var (
	v, quick       bool
	concurrency    int
	zones          []string
	prefixes       []string
	_, _file, _, _ = runtime.Caller(0)
	_dir           = filepath.Dir(_file)
)

func init() {
	flag.BoolVar(&v, "v", false, "verbose output (to stderr)")
	flag.BoolVar(&quick, "quick", false, "Only work on a subset of zones")
	flag.IntVar(&concurrency, "concurrency", 32, "Set maximum number of concurrent requests")
}

func main() {
	flag.Parse()

	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	var err error
	zones, err = readLines("zones.txt")
	if err != nil {
		return err
	}
	prefixes, err = readLines("prefixes.txt")
	if err != nil {
		return err
	}

	// Quick for debugging?
	if quick {
		fmt.Fprintf(os.Stderr, "Quick mode enabled\n")
		zones = []string{"com", "net", "org", "co", "io", "nr"}
		concurrency = 4 // Don’t slam the .org whois server
	}

	re := regexp.MustCompile(`^[^\.]+\.`)

	domains := make(map[string]bool, len(zones)*len(prefixes))
	for _, zone := range zones {
		for _, prefix := range prefixes {
			domain := prefix + "." + zone
			domains[domain] = true
			req, err := whois.Resolve(domain)
			if err == nil {
				hostParent := re.ReplaceAllLiteralString(req.Host, "")
				if _, ok := domains[hostParent]; !ok && hostParent != "" {
					fmt.Fprintf(os.Stderr, "  + %s\n", hostParent)
					domains[hostParent] = true
				}
			}
		}
	}

	fmt.Fprintf(os.Stderr, "Querying whois for %d domains (%d prefixes × %d zones + extras)\n", len(domains), len(prefixes), len(zones))

	responses := make(chan *whois.Response)
	limiter := make(chan struct{}, concurrency) // semaphore to limit concurrency
	for domain, _ := range domains {
		go func(domain string) {
			var res *whois.Response

			limiter <- struct{}{} // acquire semaphore
			defer func() {        // release semaphore
				responses <- res
				<-limiter
			}()

			req, err := whois.Resolve(domain)
			if err != nil {
				return
			}

			if v {
				fmt.Fprintf(os.Stderr, "Fetching %s from %s\n", req.Query, req.Host)
			}
			res, err = req.Fetch()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error fetching whois for %s: %s\n", req.Query, err)
				return
			}
		}(domain)
	}

	// Collect from goroutines
	var wg sync.WaitGroup
	wg.Add(len(domains))
	for i := 0; i < len(domains); i++ {
		go func() {
			res := <-responses
			defer wg.Done()

			if res == nil {
				return
			}

			if res.Host == "" {
				fmt.Fprintf(os.Stderr, "Response for %q had no host\n", res.Query)
				return
			}

			if len(res.Body) == 0 {
				fmt.Fprintf(os.Stderr, "Response for %q had empty body\n", res.Query)
				return
			}

			dir := filepath.Join(_dir, "data", "responses", res.Host)
			err := os.MkdirAll(dir, os.ModePerm)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error creating response directory for %s: %s\n", res.Host, err)
				return
			}

			fn := filepath.Join(dir, (sha1hex(res.Body) + ".mime"))
			f, err := os.Create(fn)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error creating response file for %s: %s\n", res.Query, err)
				return
			}
			defer f.Close()
			res.WriteMIME(f)
		}()
	}
	wg.Wait()

	return nil
}

var whitespaceAndComments = regexp.MustCompile(`\s+|#.+$`)

func readLines(fn string) ([]string, error) {
	fmt.Fprintf(os.Stderr, "Reading %s\n", fn)
	f, err := os.Open(filepath.Join(_dir, "data", fn))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var out []string
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := whitespaceAndComments.ReplaceAllLiteralString(s.Text(), "")
		if line == "" {
			continue
		}
		if line, ierr := idna.ToASCII(line); ierr == nil {
			out = append(out, line)
		}
	}
	return out, s.Err()
}

func sha1hex(buf []byte) string {
	h := sha1.New()
	h.Write(buf)
	return strings.ToLower(hex.EncodeToString(h.Sum(nil)))
}
<commit_msg>Support querying single zone.<commit_after>package main

import (
	"bufio"
	"crypto/sha1"
	"encoding/hex"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"sync"

	"code.google.com/p/go.net/idna"
	_ "github.com/domainr/go-whois/servers"
	"github.com/domainr/go-whois/whois"
)

var (
	v, quick       bool
	oneZone        string
	concurrency    int
	zones          []string
	prefixes       []string
	_, _file, _, _ = runtime.Caller(0)
	_dir           = filepath.Dir(_file)
)

func init() {
	flag.BoolVar(&v, "v", false, "verbose output (to stderr)")
	flag.BoolVar(&quick, "quick", false, "Only query a shorter subset of zones")
	flag.StringVar(&oneZone, "zone", "", "Only query a specific zone")
	flag.IntVar(&concurrency, "concurrency", 32, "Set maximum number of concurrent requests")
}

func main() {
	flag.Parse()

	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	var err error
	zones, err = readLines("zones.txt")
	if err != nil {
		return err
	}
	prefixes, err = readLines("prefixes.txt")
	if err != nil {
		return err
	}

	// Quick for debugging?
	if quick {
		fmt.Fprintf(os.Stderr, "Quick mode enabled\n")
		zones = []string{"com", "net", "org", "co", "io", "nr"}
		concurrency = 4 // Don’t slam the .org whois server
	}

	// One zone?
	if oneZone != "" {
		fmt.Fprintf(os.Stderr, "Querying single zone: %s\n", oneZone)
		zones = []string{oneZone}
		concurrency = 1
	}

	re := regexp.MustCompile(`^[^\.]+\.`)

	domains := make(map[string]bool, len(zones)*len(prefixes))
	for _, zone := range zones {
		for _, prefix := range prefixes {
			domain := prefix + "." + zone
			domains[domain] = true
			req, err := whois.Resolve(domain)
			if err == nil {
				hostParent := re.ReplaceAllLiteralString(req.Host, "")
				if _, ok := domains[hostParent]; !ok && hostParent != "" {
					fmt.Fprintf(os.Stderr, "  + %s\n", hostParent)
					domains[hostParent] = true
				}
			}
		}
	}

	fmt.Fprintf(os.Stderr, "Querying whois for %d domains (%d prefixes × %d zones + extras)\n", len(domains), len(prefixes), len(zones))

	responses := make(chan *whois.Response)
	limiter := make(chan struct{}, concurrency) // semaphore to limit concurrency
	for domain, _ := range domains {
		go func(domain string) {
			var res *whois.Response

			limiter <- struct{}{} // acquire semaphore
			defer func() {        // release semaphore
				responses <- res
				<-limiter
			}()

			req, err := whois.Resolve(domain)
			if err != nil {
				return
			}

			if v {
				fmt.Fprintf(os.Stderr, "Fetching %s from %s\n", req.Query, req.Host)
			}
			res, err = req.Fetch()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error fetching whois for %s: %s\n", req.Query, err)
				return
			}
		}(domain)
	}

	// Collect from goroutines
	var wg sync.WaitGroup
	wg.Add(len(domains))
	for i := 0; i < len(domains); i++ {
		go func() {
			res := <-responses
			defer wg.Done()

			if res == nil {
				return
			}

			if res.Host == "" {
				fmt.Fprintf(os.Stderr, "Response for %q had no host\n", res.Query)
				return
			}

			if len(res.Body) == 0 {
				fmt.Fprintf(os.Stderr, "Response for %q had empty body\n", res.Query)
				return
			}

			dir := filepath.Join(_dir, "data", "responses", res.Host)
			err := os.MkdirAll(dir, os.ModePerm)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error creating response directory for %s: %s\n", res.Host, err)
				return
			}

			fn := filepath.Join(dir, (sha1hex(res.Body) + ".mime"))
			f, err := os.Create(fn)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error creating response file for %s: %s\n", res.Query, err)
				return
			}
			defer f.Close()
			res.WriteMIME(f)
		}()
	}
	wg.Wait()

	return nil
}

var whitespaceAndComments = regexp.MustCompile(`\s+|#.+$`)

func readLines(fn string) ([]string, error) {
	fmt.Fprintf(os.Stderr, "Reading %s\n", fn)
	f, err := os.Open(filepath.Join(_dir, "data", fn))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var out []string
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := whitespaceAndComments.ReplaceAllLiteralString(s.Text(), "")
		if line == "" {
			continue
		}
		if line, ierr := idna.ToASCII(line); ierr == nil {
			out = append(out, line)
		}
	}
	return out, s.Err()
}

func sha1hex(buf []byte) string {
	h := sha1.New()
	h.Write(buf)
	return strings.ToLower(hex.EncodeToString(h.Sum(nil)))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/mux"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig() {

	viper.SetConfigType("yaml")
	viper.SetConfigName("config")

	viper.AddConfigPath("$HOME/.gotator")
	viper.AddConfigPath(".")

	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("ip")
	viper.BindEnv("port")

	if !viper.IsSet("ip") || !viper.IsSet("port") {
		fmt.Fprintln(os.Stderr, "Configuration error.  Both IP and PORT must be set via either config or environment.")
		os.Exit(1)
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("\nConfig file changed:", e.Name)
		skip <- struct{}{}
		fmt.Printf("Content will change immediately.\n\n")
	})

}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	fmt.Printf("Loading programs from %s\n", filename)

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))
	r.LazyQuotes = true

	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			fmt.Printf("Finished loading programs.\n\n")
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		if err != nil {
			fmt.Println("Program rejected.  Invalid duration.")
		}

		fmt.Printf("  Loaded program %.50s to show for %s.\n", p.URL, p.Duration)
		list = append(list, p)
	}

	return list
}

func runProgram(program Program) {

	ip := viper.Get("IP")
	port := viper.GetInt("PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	conn, err := net.Dial("tcp", constr)
	if err != nil {
		fmt.Printf("Error making network connection to: %s\n", constr)
		fmt.Println("Pausing for 30s")
		time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
		return
	}

	log.Printf("Running program for %s:  %s\n", program.Duration, program.URL)
	fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
	status, err := bufio.NewReader(conn).ReadString('\n')
	fmt.Printf("  %s", status)

	select {
	case <-time.After(program.Duration):
		// Do nothing.
		pause = false
	case <-skip:
		fmt.Println("Current program skiped")
		return
	}
}

func LoadAndRunLoop() {

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")
		for pause == true {
			fmt.Println("Paused.")
			time.Sleep(1 * time.Second)
		}

		pl := loadProgramList(filename)

		for _, p := range pl {
			for pause == true {
				fmt.Println("Program list is paused.")
				time.Sleep(1 * time.Second)
			}
			runProgram(p)
		}

		fmt.Printf("\nLooping back to play program list from beginning\n\n")
	}

}

func PlayHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	var p Program
	p.URL = r.Form.Get("url")
	fmt.Printf("URL: %s\n", p.URL)

	d := r.Form.Get("duration")
	fmt.Printf("Duration: %s\n", d)

	var err error
	p.Duration, err = time.ParseDuration(r.Form.Get("duration"))
	if err != nil {
		w.Write([]byte("Program rejected.  Invalid duration.\n"))
		return
	}

	// Needs validation...

	// Now do something with the program.. play it?

	// Stop normal rotation
	pause = true
	skip <- struct{}{}

	go runProgram(p)
	w.Write([]byte("Program accepted\n"))

}

func PauseHandler(w http.ResponseWriter, r *http.Request) {
	pause = true
	log.Println("Pausing from web request")
	w.Write([]byte("Ok, paused.\n"))
}

func ResumeHandler(w http.ResponseWriter, r *http.Request) {
	pause = false
	log.Println("Unpausing from web request")
	w.Write([]byte("Ok, unpaused.\n"))
}
func SkipHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Skippingfrom web request")
	pause = false
	skip <- struct{}{}

	w.Write([]byte("Skipping current programming and resume program list runner from web request.\n"))
}

func readKeyboardLoop() {
	for {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		fmt.Printf(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n\n")
		pause = false
		skip <- struct{}{}
	}
}

// Control channel to stop running programs immediately (yes, global)
var skip = make(chan struct{})
var pause bool = false

func main() {

	InitializeConfig()

	go LoadAndRunLoop()
	go readKeyboardLoop()

	r := mux.NewRouter()
	r.HandleFunc("/play", PlayHandler)
	r.HandleFunc("/pause", PauseHandler)
	r.HandleFunc("/resume", ResumeHandler)
	r.HandleFunc("/skip", SkipHandler)

	go log.Fatal(http.ListenAndServe(":8080", r))

}
<commit_msg>Added gotator specifc output when CSV read fails.<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/mux"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig() {

	viper.SetConfigType("yaml")
	viper.SetConfigName("config")

	viper.AddConfigPath("$HOME/.gotator")
	viper.AddConfigPath(".")

	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("ip")
	viper.BindEnv("port")

	if !viper.IsSet("ip") || !viper.IsSet("port") {
		fmt.Fprintln(os.Stderr, "Configuration error.  Both IP and PORT must be set via either config or environment.")
		os.Exit(1)
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("\nConfig file changed:", e.Name)
		skip <- struct{}{}
		fmt.Printf("Content will change immediately.\n\n")
	})

}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	fmt.Printf("Loading programs from %s\n", filename)

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))
	r.LazyQuotes = true

	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			fmt.Printf("Finished loading programs.\n\n")
			break
		}
		if err != nil {
			log.Printf("Error reading line from program file: %s.  Abandoning attempt to read programs.\n", filename)
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		if err != nil {
			fmt.Println("Program rejected.  Invalid duration.")
		}

		fmt.Printf("  Loaded program %.50s to show for %s.\n", p.URL, p.Duration)
		list = append(list, p)
	}

	return list
}

func runProgram(program Program) {

	ip := viper.Get("IP")
	port := viper.GetInt("PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	conn, err := net.Dial("tcp", constr)
	if err != nil {
		fmt.Printf("Error making network connection to: %s\n", constr)
		fmt.Println("Pausing for 30s")
		time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
		return
	}

	log.Printf("Running program for %s:  %s\n", program.Duration, program.URL)
	fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
	status, err := bufio.NewReader(conn).ReadString('\n')
	fmt.Printf("  %s", status)

	select {
	case <-time.After(program.Duration):
		// Do nothing.
		pause = false
	case <-skip:
		fmt.Println("Current program skiped")
		return
	}
}

func LoadAndRunLoop() {

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")
		for pause == true {
			fmt.Println("Paused.")
			time.Sleep(1 * time.Second)
		}

		pl := loadProgramList(filename)

		for _, p := range pl {
			for pause == true {
				fmt.Println("Program list is paused.")
				time.Sleep(1 * time.Second)
			}
			runProgram(p)
		}

		fmt.Printf("\nLooping back to play program list from beginning\n\n")
	}

}

func PlayHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	var p Program
	p.URL = r.Form.Get("url")
	fmt.Printf("URL: %s\n", p.URL)

	d := r.Form.Get("duration")
	fmt.Printf("Duration: %s\n", d)

	var err error
	p.Duration, err = time.ParseDuration(r.Form.Get("duration"))
	if err != nil {
		w.Write([]byte("Program rejected.  Invalid duration.\n"))
		return
	}

	// Needs validation...

	// Now do something with the program.. play it?

	// Stop normal rotation
	pause = true
	skip <- struct{}{}

	go runProgram(p)
	w.Write([]byte("Program accepted\n"))

}

func PauseHandler(w http.ResponseWriter, r *http.Request) {
	pause = true
	log.Println("Pausing from web request")
	w.Write([]byte("Ok, paused.\n"))
}

func ResumeHandler(w http.ResponseWriter, r *http.Request) {
	pause = false
	log.Println("Unpausing from web request")
	w.Write([]byte("Ok, unpaused.\n"))
}
func SkipHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Skippingfrom web request")
	pause = false
	skip <- struct{}{}

	w.Write([]byte("Skipping current programming and resume program list runner from web request.\n"))
}

func readKeyboardLoop() {
	for {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		fmt.Printf(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n\n")
		pause = false
		skip <- struct{}{}
	}
}

// Control channel to stop running programs immediately (yes, global)
var skip = make(chan struct{})
var pause bool = false

func main() {

	InitializeConfig()

	go LoadAndRunLoop()
	go readKeyboardLoop()

	r := mux.NewRouter()
	r.HandleFunc("/play", PlayHandler)
	r.HandleFunc("/pause", PauseHandler)
	r.HandleFunc("/resume", ResumeHandler)
	r.HandleFunc("/skip", SkipHandler)

	go log.Fatal(http.ListenAndServe(":8080", r))

}
<|endoftext|>


<commit_before>package main

import(
	"fmt"
	"net/http"
	"net/url"
	"os"
   "github.com/Jeffail/gabs" // more easier json parsing
	"io/ioutil"
	"strings"
	)

func helpHandler(w http.ResponseWriter, r *http.Request) {
// Help screen for those accessing url '/'
   fmt.Fprintf(w, "Welcome to BijBling\nValid targets:\n\n")
   fmt.Fprintf(w, "/alert :: reflects websocket alerts from Librato\n")
}

func alertHandler(w http.ResponseWriter, r *http.Request) {
// Handler for url path '/alert', this transforms an alert from librato
// into an annotation stream back inside librato
	luser := os.Getenv("BIJ_USER")
   lpass := os.Getenv("BIJ_TOKEN")

	//panic if we don't have librato credentials
	if luser == `` || lpass == `` {
		fmt.Printf("ERROR::Export BIJ_USER and BIJ_TOKEN with your librato credentials")
		os.Exit(42)
	}

	//parse out the server name and alert name from the alert payload
	payload,_ := ioutil.ReadAll(r.Body)
	parsed_payload,_ := gabs.ParseJSON(payload)
   alert, _ := parsed_payload.Path("alert.name").Data().(string)
	url_path := fmt.Sprintf("https://metrics-api.librato.com/v1/annotations/alerts.%s", alert)

   violators, _ := parsed_payload.S("violations").ChildrenMap()
	for host, _ := range violators {
		//for each host that violated the threshold, fire off an annotation
		//into the annotation stream named for this alert
		form := url.Values{}
		form.Set("title", fmt.Sprintf("Alert Fired :: %s ",alert))
		form.Set("source", host)
		form.Set("description", fmt.Sprintf("Alert: %s fired on host %s",alert, host))
		req,_ := http.NewRequest("POST", url_path, strings.NewReader(form.Encode()))
		defer req.Body.Close()
		req.SetBasicAuth(luser,lpass)
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

		fmt.Printf("Alert: %s fired on host %s\n", alert, host)
		//post the annotation
		client := new(http.Client)
		resp, err := client.Do(req)

		//log the response from librato-api
		if err != nil {
			fmt.Printf("ERROR :: %s\n",err)
		}
		resp_body,_ := ioutil.ReadAll(resp.Body)
		fmt.Printf("Annotation response: %s\n", resp.Status)
		fmt.Printf("Annotation response body: %s\n", resp_body)
	}
}

//lets kick this pig
func main() {
	fmt.Println("Started")
   http.HandleFunc("/", helpHandler)
   http.HandleFunc("/alert", alertHandler)
   http.ListenAndServe(":80", nil)
}
<commit_msg>fixing for herokus port env var<commit_after>package main

import(
	"fmt"
	"net/http"
	"net/url"
	"os"
   "github.com/Jeffail/gabs" // more easier json parsing
	"io/ioutil"
	"strings"
	)

func helpHandler(w http.ResponseWriter, r *http.Request) {
// Help screen for those accessing url '/'
   fmt.Fprintf(w, "Welcome to BijBling\nValid targets:\n\n")
   fmt.Fprintf(w, "/alert :: reflects websocket alerts from Librato\n")
}

func alertHandler(w http.ResponseWriter, r *http.Request) {
// Handler for url path '/alert', this transforms an alert from librato
// into an annotation stream back inside librato
	luser := os.Getenv("BIJ_USER")
   lpass := os.Getenv("BIJ_TOKEN")

	//panic if we don't have librato credentials
	if luser == `` || lpass == `` {
		fmt.Printf("ERROR::Export BIJ_USER and BIJ_TOKEN with your librato credentials")
	}

	//parse out the server name and alert name from the alert payload
	payload,_ := ioutil.ReadAll(r.Body)
	parsed_payload,_ := gabs.ParseJSON(payload)
   alert, _ := parsed_payload.Path("alert.name").Data().(string)
	url_path := fmt.Sprintf("https://metrics-api.librato.com/v1/annotations/alerts.%s", alert)

   violators, _ := parsed_payload.S("violations").ChildrenMap()
	for host, _ := range violators {
		//for each host that violated the threshold, fire off an annotation
		//into the annotation stream named for this alert
		form := url.Values{}
		form.Set("title", fmt.Sprintf("Alert Fired :: %s ",alert))
		form.Set("source", host)
		form.Set("description", fmt.Sprintf("Alert: %s fired on host %s",alert, host))
		req,_ := http.NewRequest("POST", url_path, strings.NewReader(form.Encode()))
		defer req.Body.Close()
		req.SetBasicAuth(luser,lpass)
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

		fmt.Printf("Alert: %s fired on host %s\n", alert, host)
		//post the annotation
		client := new(http.Client)
		resp, err := client.Do(req)

		//log the response from librato-api
		if err != nil {
			fmt.Printf("ERROR :: %s\n",err)
		}
		resp_body,_ := ioutil.ReadAll(resp.Body)
		fmt.Printf("Annotation response: %s\n", resp.Status)
		fmt.Printf("Annotation response body: %s\n", resp_body)
	}
}

//lets kick this pig
func main() {
	port := fmt.Sprintf(":%s",os.Getenv("PORT"))
	fmt.Printf("Starting on port %s",port)
   http.HandleFunc("/", helpHandler)
   http.HandleFunc("/alert", alertHandler)
   http.ListenAndServe(port, nil)
}
<|endoftext|>