<commit_before>// Command cache-pkgs caches pacakge directories based on the hash of
// dependency specification file. Unix only.
//
//     Usage:
//        cache-pkgs [opts] <dep-spec-file> <dir> <cmd> [args..]
//
//     Caches output directory (dir) based on the hash of the dependency
//     specification file. If the specification changes the output directory
//     is regenerated using cmd and the args. Useful in CI settings.
//
//     Example:
//        cache-pkgs package.json node_modules npm install
//
//     Options can be:
//       -clean
//         	Clean cache and exit
//       -f	Force remove existing output directory
//       -symlink
//         	Use a symlink instead of copy (default true)
package main

import (
	"crypto/sha1"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"strings"
	"time"
)

var (
	symlink = flag.Bool("symlink", true, "Use a symlink instead of copy")
	force   = flag.Bool("f", false, "Force remove existing output directory")
	clean   = flag.Bool("clean", false, "Clean cache and exit")
)

func usage() {
	usageStr := `Usage:
   %s [opts] <dep-spec-file> <dir> <cmd> [args..]

Caches output directory (dir) based on the hash of the dependency
specification file. If the specification changes the output directory
is regenerated using cmd and the args. Useful in CI settings.

Example:
   %s package.json node_modules npm install

Options can be:
`
	me := filepath.Base(os.Args[0])
	fmt.Fprintf(os.Stderr, usageStr, me, me)
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	cacheStore, err := cacheDir("")
	if err != nil {
		exitWith("Cache dir problems: ", err)
	}

	if *clean {
		fmt.Printf("Wiping cache %q\n", cacheStore)
		err := os.RemoveAll(cacheStore)
		if err != nil {
			exitWith(err)
		}
		return
	}

	if flag.NArg() < 3 {
		exitUsage("please supply both dependency description file, outputdir and the command to generate it")
	}

	depDesc := flag.Arg(0)
	outputdir := flag.Arg(1)
	cmd := flag.Args()[2]
	args := flag.Args()[3:]

	h, err := hashFile(depDesc)
	if err != nil {
		exitWith("Can't hash dependency description:", err)
	}

	depDir := path.Join(cacheStore, h)

	// pre build
	if *force {
		err := os.RemoveAll(outputdir)
		if err != nil && err != os.ErrNotExist {
			exitWith("Error trying to remove existing output dir", err)
		}
	} else {
		_, err := os.Stat(outputdir)
		if !os.IsNotExist(err) {
			exitWith("output path '", outputdir, "' already exists - maybe rerun with `-f`")
		}
	}

	cached, err := IsDir(depDir)
	if err != nil {
		exitWith("Error looking up cache dir", err)
	}

	// build
	start := time.Now()
	if cached {
		Progress("Found cached dependencies - installing those")
		err = Install(depDir, outputdir, *symlink)
	} else {
		Progressf("Running `%s %s` and caching the output", cmd, strings.Join(args, " "))
		err = GenerateAndCache(depDir, outputdir, cmd, args)
	}

	if err != nil {
		exitWith(err)
	}

	Progressf("Succeeded in %.2f sec", time.Now().Sub(start).Seconds())
}

func Install(from, to string, link bool) (err error) {
	from, err = filepath.Abs(from)
	if err != nil {
		return err
	}
	to, err = filepath.Abs(to)
	if err != nil {
		return err
	}

	if link {
		// to is a symlink to from
		err = os.Symlink(from, to)
	} else {
		err = Copy(from, to)
	}
	return err
}

func IsDir(d string) (bool, error) {
	info, err := os.Stat(d)
	if os.IsNotExist(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}

	return info.IsDir(), nil

}

func run(bin string, args ...string) error {
	cmd := exec.Command(bin, args...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	return cmd.Run()
}

func GenerateAndCache(cache, outputdir, cmd string, args []string) error {
	err := run(cmd, args...)
	if err != nil {
		return err
	}
	return Copy(outputdir, cache)
}

//
func Copy(a, b string) error {
	return run("cp", "-R", a, b)
}

func exitUsage(a ...interface{}) {
	flag.Usage()
	exitWith(a...)
}
func exitWith(a ...interface{}) {
	fmt.Fprint(os.Stderr, append([]interface{}{"Error: "}, append(a, "\n")...)...)
	os.Exit(1)
}

func hashFile(fname string) (hash string, err error) {
	h := sha1.New()
	f, err := os.Open(fname)
	if err != nil {
		return "", err
	}
	defer f.Close()

	_, err = io.Copy(h, f)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func ensureDir(dir string) error {

	info, err := os.Stat(dir)
	if os.IsNotExist(err) {
		Progress("creating cache dir", dir)
		return os.MkdirAll(dir, 0750)
	}
	if err != nil {
		return err
	}
	if !info.IsDir() {
		return errors.New(dir + " exists but is not a dir")
	}
	return nil
}

func cacheDir(dirName string) (dir string, err error) {

	if dirName == "" {
		dir = dirName
	}

	if dir == "" {
		dir = os.Getenv("CACHE_DIR")
	}

	if dir == "" {
		u, err := user.Current()
		if err != nil {
			return "", err
		}

		dir = path.Join(u.HomeDir, ".dep-cache")
	}

	err = ensureDir(dir)
	if err != nil {
		return "", err
	}
	return dir, nil
}

func Progressf(format string, a ...interface{}) {
	ProgressPrint(fmt.Sprintf(format, a...))
}

func Progress(a ...interface{}) {
	ProgressPrint(fmt.Sprint(a...))
}

func ProgressPrint(s string) {
	prefix := os.Getenv("PREFIX")
	fmt.Fprintf(os.Stderr, "%s%s\n", prefix, s)
}
<commit_msg>Fall back to os/user only if HOME not set<commit_after>// Command cache-pkgs caches pacakge directories based on the hash of
// dependency specification file. Unix only.
//
//     Usage:
//        cache-pkgs [opts] <dep-spec-file> <dir> <cmd> [args..]
//
//     Caches output directory (dir) based on the hash of the dependency
//     specification file. If the specification changes the output directory
//     is regenerated using cmd and the args. Useful in CI settings.
//
//     Example:
//        cache-pkgs package.json node_modules npm install
//
//     Options can be:
//       -clean
//         	Clean cache and exit
//       -f	Force remove existing output directory
//       -symlink
//         	Use a symlink instead of copy (default true)
package main

import (
	"crypto/sha1"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"strings"
	"time"
)

var (
	symlink = flag.Bool("symlink", true, "Use a symlink instead of copy")
	force   = flag.Bool("f", false, "Force remove existing output directory")
	clean   = flag.Bool("clean", false, "Clean cache and exit")
)

func usage() {
	usageStr := `Usage:
   %s [opts] <dep-spec-file> <dir> <cmd> [args..]

Caches output directory (dir) based on the hash of the dependency
specification file. If the specification changes the output directory
is regenerated using cmd and the args. Useful in CI settings.

Example:
   %s package.json node_modules npm install

Options can be:
`
	me := filepath.Base(os.Args[0])
	fmt.Fprintf(os.Stderr, usageStr, me, me)
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	cacheStore, err := cacheDir("")
	if err != nil {
		exitWith("Cache dir problems: ", err)
	}

	if *clean {
		fmt.Printf("Wiping cache %q\n", cacheStore)
		err := os.RemoveAll(cacheStore)
		if err != nil {
			exitWith(err)
		}
		return
	}

	if flag.NArg() < 3 {
		exitUsage("please supply both dependency description file, outputdir and the command to generate it")
	}

	depDesc := flag.Arg(0)
	outputdir := flag.Arg(1)
	cmd := flag.Args()[2]
	args := flag.Args()[3:]

	h, err := hashFile(depDesc)
	if err != nil {
		exitWith("Can't hash dependency description:", err)
	}

	depDir := path.Join(cacheStore, h)

	// pre build
	if *force {
		err := os.RemoveAll(outputdir)
		if err != nil && err != os.ErrNotExist {
			exitWith("Error trying to remove existing output dir", err)
		}
	} else {
		_, err := os.Stat(outputdir)
		if !os.IsNotExist(err) {
			exitWith("output path '", outputdir, "' already exists - maybe rerun with `-f`")
		}
	}

	cached, err := IsDir(depDir)
	if err != nil {
		exitWith("Error looking up cache dir", err)
	}

	// build
	start := time.Now()
	if cached {
		Progress("Found cached dependencies - installing those")
		err = Install(depDir, outputdir, *symlink)
	} else {
		Progressf("Running `%s %s` and caching the output", cmd, strings.Join(args, " "))
		err = GenerateAndCache(depDir, outputdir, cmd, args)
	}

	if err != nil {
		exitWith(err)
	}

	Progressf("Succeeded in %.2f sec", time.Now().Sub(start).Seconds())
}

func Install(from, to string, link bool) (err error) {
	from, err = filepath.Abs(from)
	if err != nil {
		return err
	}
	to, err = filepath.Abs(to)
	if err != nil {
		return err
	}

	if link {
		// to is a symlink to from
		err = os.Symlink(from, to)
	} else {
		err = Copy(from, to)
	}
	return err
}

func IsDir(d string) (bool, error) {
	info, err := os.Stat(d)
	if os.IsNotExist(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}

	return info.IsDir(), nil

}

func run(bin string, args ...string) error {
	cmd := exec.Command(bin, args...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	return cmd.Run()
}

func GenerateAndCache(cache, outputdir, cmd string, args []string) error {
	err := run(cmd, args...)
	if err != nil {
		return err
	}
	return Copy(outputdir, cache)
}

//
func Copy(a, b string) error {
	return run("cp", "-R", a, b)
}

func exitUsage(a ...interface{}) {
	flag.Usage()
	exitWith(a...)
}
func exitWith(a ...interface{}) {
	fmt.Fprint(os.Stderr, append([]interface{}{"Error: "}, append(a, "\n")...)...)
	os.Exit(1)
}

func hashFile(fname string) (hash string, err error) {
	h := sha1.New()
	f, err := os.Open(fname)
	if err != nil {
		return "", err
	}
	defer f.Close()

	_, err = io.Copy(h, f)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func ensureDir(dir string) error {

	info, err := os.Stat(dir)
	if os.IsNotExist(err) {
		Progress("creating cache dir", dir)
		return os.MkdirAll(dir, 0750)
	}
	if err != nil {
		return err
	}
	if !info.IsDir() {
		return errors.New(dir + " exists but is not a dir")
	}
	return nil
}

func cacheDir(dirName string) (dir string, err error) {

	if dirName == "" {
		dir = dirName
	}

	if dir == "" {
		dir = os.Getenv("CACHE_DIR")
	}

	if dir == "" {
		home := os.Getenv("HOME")
		if home == "" {
			// os/user uses cgo. Fails under cross-compile.
			u, err := user.Current()
			if err != nil {
				return "", err
			}
			home = u.HomeDir
		}
		dir = path.Join(home, ".dep-cache")
	}

	err = ensureDir(dir)
	if err != nil {
		return "", err
	}
	return dir, nil
}

func Progressf(format string, a ...interface{}) {
	ProgressPrint(fmt.Sprintf(format, a...))
}

func Progress(a ...interface{}) {
	ProgressPrint(fmt.Sprint(a...))
}

func ProgressPrint(s string) {
	prefix := os.Getenv("PREFIX")
	fmt.Fprintf(os.Stderr, "%s%s\n", prefix, s)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"code.google.com/p/gopacket"
	"code.google.com/p/gopacket/layers"
	"code.google.com/p/gopacket/pcap"
	"code.google.com/p/gopacket/tcpassembly"
	"code.google.com/p/gopacket/tcpassembly/tcpreader"
	"flag"
	"io"
	"log"
	"net/http"
)

/* Command line arguments */
var iface = flag.String("i", "lo0", "Interface to get packets from")
var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail")

// httpStream will handle the actual decoding of http requests and responses.
type httpStream struct {
	netFlow, tcpFlow gopacket.Flow
	readStream       tcpreader.ReaderStream
}

/* This reads both HTTP requests and HTTP responses in two separate streams */
func (h *httpStream) run() {
	log.Printf("run")
	log.Println("reading stream", h.netFlow, h.tcpFlow)
	buf := bufio.NewReader(&h.readStream)
	for {
		req, err := http.ReadRequest(buf)
		if err == io.EOF {
			// We must read until we see an EOF... very important!
			return
		} else if err != nil {
			log.Println("Error reading stream", h.netFlow, h.tcpFlow, ":", err)
		} else {
			bodyBytes := tcpreader.DiscardBytesToEOF(req.Body)
			req.Body.Close()
			log.Println("Received request from stream", h.netFlow, h.tcpFlow, ":", req, "with", bodyBytes, "bytes in request body")
		}
	}
}

/* httpStreamFactory implements tcpassembly.StreamFactory */
type httpStreamFactory struct{}

func (h *httpStreamFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {
	hstream := &httpStream{
		netFlow:    netFlow,
		tcpFlow:    tcpFlow,
		readStream: tcpreader.NewReaderStream(),
	}
	go hstream.run() // Important... we must guarantee that data from the reader stream is read.

	// ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
	return &hstream.readStream
}

func main() {
	flag.Parse()
	log.Printf("starting capture on interface %q", *iface)

	handle, err := pcap.OpenLive(*iface, 1600, true, pcap.BlockForever)
	if err != nil {
		panic(err)
	} else if err := handle.SetBPFFilter("tcp and port 30080"); err != nil {
		panic(err)
	}

	// Set up assembly
	streamFactory := &httpStreamFactory{}
	streamPool := tcpassembly.NewStreamPool(streamFactory)
	assembler := tcpassembly.NewAssembler(streamPool)

	log.Println("reading in packets")
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	packets := packetSource.Packets()

	for packet := range packets {

		if *logAllPackets {
			log.Println(packet)
		}

		tcp := packet.TransportLayer().(*layers.TCP)
		assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp,
			packet.Metadata().Timestamp)
	}
}
<commit_msg>Separate streams in 'in' and 'out'. Read http responses from the 'in' stream.<commit_after>package main

import (
	"bufio"
	"code.google.com/p/gopacket"
	"code.google.com/p/gopacket/layers"
	"code.google.com/p/gopacket/pcap"
	"code.google.com/p/gopacket/tcpassembly"
	"code.google.com/p/gopacket/tcpassembly/tcpreader"
	"flag"
	"io"
	"log"
	"net/http"
)

/* Command line arguments */
var iface = flag.String("i", "lo0", "Interface to get packets from")
var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail")

type bidiStream struct {
	key     uint64
	in, out *httpStream
}

// httpStream will handle the actual decoding of http requests and responses.
type httpStream struct {
	netFlow, tcpFlow gopacket.Flow
	readStream       tcpreader.ReaderStream
}

/* This reads both HTTP requests and HTTP responses in two separate streams */
func (h *httpStream) runOut() {
	log.Printf("runOut")

	buf := bufio.NewReader(&h.readStream)
	for {
		req, err := http.ReadRequest(buf)
		if err == io.EOF {
			// We must read until we see an EOF... very important!
			return
		} else if err != nil {
			log.Println("Error reading stream", h.netFlow, h.tcpFlow, ":", err)
		} else {
			bodyBytes := tcpreader.DiscardBytesToEOF(req.Body)
			req.Body.Close()
			log.Println("Received request from stream", h.netFlow, h.tcpFlow,
				":", req, "with", bodyBytes, "bytes in request body")
		}
	}
}

func (h *httpStream) runIn() {
	log.Printf("runIn")

	buf := bufio.NewReader(&h.readStream)
	for {

		/* Don't start reading a response if no data is available */
		_, err := buf.Peek(1)
		if err == io.EOF {
			return
		}
		/* Data available, read response */
		resp, err := http.ReadResponse(buf, nil) // TODO: request
		if err == io.EOF {
			// We must read until we see an EOF... very important!
			log.Println("EOF while reading stream", h.netFlow, h.tcpFlow, ":", err)
			return
		} else if err != nil {
			log.Println("Error reading stream", h.netFlow, h.tcpFlow, ":", err)
		} else {
			bodyBytes := tcpreader.DiscardBytesToEOF(resp.Body)
			resp.Body.Close()
			log.Println("Received response from stream", h.netFlow, h.tcpFlow,
				":", resp, "with", bodyBytes, "bytes in response body")
		}

		/* Match the response with the next request */
	}

}

/* httpStreamFactory implements tcpassembly.StreamFactory */
type httpStreamFactory struct {
	bidiStreams map[uint64]*bidiStream
}

func (h *httpStreamFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {

	/* Can we find out if this is the outgoing or incoming stream? */
	/* First the outgoing stream, then the incoming stream */
	key := netFlow.FastHash() ^ tcpFlow.FastHash()

	hstream := &httpStream{
		netFlow:    netFlow,
		tcpFlow:    tcpFlow,
		readStream: tcpreader.NewReaderStream(),
	}

	bds := h.bidiStreams[key]
	if bds == nil {
		log.Println("new stream", key)
		bds = &bidiStream{in: hstream, key: key}
		h.bidiStreams[key] = bds
		go hstream.runOut() // Important... we must guarantee that data from the reader stream is read.
	} else {
		bds.out = hstream
		go hstream.runIn() // Important... we must guarantee that data from the reader stream is read.
	}

	// ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
	return &hstream.readStream
}

func main() {
	flag.Parse()
	log.Printf("starting capture on interface %q", *iface)

	handle, err := pcap.OpenLive(*iface, 1600, true, pcap.BlockForever)
	if err != nil {
		panic(err)
	} else if err := handle.SetBPFFilter("tcp and port 30080"); err != nil {
		panic(err)
	}

	// Set up assembly
	streamFactory := &httpStreamFactory{bidiStreams: make(map[uint64]*bidiStream)}
	streamPool := tcpassembly.NewStreamPool(streamFactory)
	assembler := tcpassembly.NewAssembler(streamPool)

	log.Println("reading in packets")
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	packets := packetSource.Packets()

	for packet := range packets {

		if *logAllPackets {
			log.Println(packet)
		}

		tcp := packet.TransportLayer().(*layers.TCP)
		assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp,
			packet.Metadata().Timestamp)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/mesos/mesos-go/detector"
	"github.com/mesosphere/mesos-dns/detect"
	"github.com/mesosphere/mesos-dns/logging"
	"github.com/mesosphere/mesos-dns/records"
	"github.com/mesosphere/mesos-dns/resolver"
	"github.com/mesosphere/mesos-dns/util"
)

func main() {
	util.PanicHandlers = append(util.PanicHandlers, func(_ interface{}) {
		// by default the handler already logs the panic
		os.Exit(1)
	})

	var versionFlag bool

	// parse flags
	cjson := flag.String("config", "config.json", "path to config file (json)")
	flag.BoolVar(&versionFlag, "version", false, "output the version")
	flag.Parse()

	// -version
	if versionFlag {
		fmt.Println(Version)
		os.Exit(0)
	}

	// initialize logging
	logging.SetupLogs()
	initAuth()

	// initialize resolver
	config := records.SetConfig(*cjson)
	res := resolver.New(Version, config)
	errch := make(chan error)

	// launch DNS server
	if config.DNSOn {
		go func() {
			logging.Verbose.Println("waiting for a reload before serving DNS")
			<-res.Ready()
			logging.Verbose.Println("reload occurred, serving DNS")
			errch <- <-res.LaunchDNS()
		}()
	}

	// launch HTTP server
	if config.HTTPOn {
		go func() {
			logging.Verbose.Println("waiting for a reload before serving HTTP")
			<-res.Ready()
			logging.Verbose.Println("reload occurred, serving HTTP")
			errch <- <-res.LaunchHTTP()
		}()
	}

	changed := detectMasters(config.Zk, config.Masters)
	reload := time.NewTicker(time.Second * time.Duration(config.RefreshSeconds))
	zkTimeout := time.Second * time.Duration(config.ZkDetectionTimeout)
	timeout := time.AfterFunc(zkTimeout, func() {
		if zkTimeout > 0 {
			errch <- fmt.Errorf("master detection timed out after %s", zkTimeout)
		}
	})

	defer reload.Stop()
	defer util.HandleCrash()
	for {
		select {
		case <-reload.C:
			res.Reload()
		case masters := <-changed:
			if len(masters) == 0 || masters[0] == "" { // no leader
				timeout.Reset(zkTimeout)
			} else {
				timeout.Stop()
			}
			logging.VeryVerbose.Printf("new masters detected: %v", masters)
			res.SetMasters(masters)
			res.Reload()
		case err := <-errch:
			logging.Error.Fatal(err)
		}
	}
}

func detectMasters(zk string, masters []string) <-chan []string {
	changed := make(chan []string, 1)
	if zk != "" {
		logging.Verbose.Println("Starting master detector for ZK ", zk)
		if md, err := detector.New(zk); err != nil {
			log.Fatalf("failed to create master detector: %v", err)
		} else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil {
			log.Fatalf("failed to initialize master detector: %v", err)
		}
	} else {
		changed <- masters
	}
	return changed
}
<commit_msg>Updated logging regarding new masters.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/mesos/mesos-go/detector"
	"github.com/mesosphere/mesos-dns/detect"
	"github.com/mesosphere/mesos-dns/logging"
	"github.com/mesosphere/mesos-dns/records"
	"github.com/mesosphere/mesos-dns/resolver"
	"github.com/mesosphere/mesos-dns/util"
)

func main() {
	util.PanicHandlers = append(util.PanicHandlers, func(_ interface{}) {
		// by default the handler already logs the panic
		os.Exit(1)
	})

	var versionFlag bool

	// parse flags
	cjson := flag.String("config", "config.json", "path to config file (json)")
	flag.BoolVar(&versionFlag, "version", false, "output the version")
	flag.Parse()

	// -version
	if versionFlag {
		fmt.Println(Version)
		os.Exit(0)
	}

	// initialize logging
	logging.SetupLogs()
	initAuth()

	// initialize resolver
	config := records.SetConfig(*cjson)
	res := resolver.New(Version, config)
	errch := make(chan error)

	// launch DNS server
	if config.DNSOn {
		go func() {
			logging.Verbose.Println("waiting for a reload before serving DNS")
			<-res.Ready()
			logging.Verbose.Println("reload occurred, serving DNS")
			errch <- <-res.LaunchDNS()
		}()
	}

	// launch HTTP server
	if config.HTTPOn {
		go func() {
			logging.Verbose.Println("waiting for a reload before serving HTTP")
			<-res.Ready()
			logging.Verbose.Println("reload occurred, serving HTTP")
			errch <- <-res.LaunchHTTP()
		}()
	}

	changed := detectMasters(config.Zk, config.Masters)
	reload := time.NewTicker(time.Second * time.Duration(config.RefreshSeconds))
	zkTimeout := time.Second * time.Duration(config.ZkDetectionTimeout)
	timeout := time.AfterFunc(zkTimeout, func() {
		if zkTimeout > 0 {
			errch <- fmt.Errorf("master detection timed out after %s", zkTimeout)
		}
	})

	defer reload.Stop()
	defer util.HandleCrash()
	for {
		select {
		case <-reload.C:
			res.Reload()
		case masters := <-changed:
			if len(masters) == 0 || masters[0] == "" { // no leader
				timeout.Reset(zkTimeout)
			} else {
				timeout.Stop()
			}
			logging.Verbose.Printf("new masters detected: %v", masters)
			res.SetMasters(masters)
			res.Reload()
		case err := <-errch:
			logging.Error.Fatal(err)
		}
	}
}

func detectMasters(zk string, masters []string) <-chan []string {
	changed := make(chan []string, 1)
	if zk != "" {
		logging.Verbose.Println("Starting master detector for ZK ", zk)
		if md, err := detector.New(zk); err != nil {
			log.Fatalf("failed to create master detector: %v", err)
		} else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil {
			log.Fatalf("failed to initialize master detector: %v", err)
		}
	} else {
		changed <- masters
	}
	return changed
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/cloudfoundry-community/firehose-to-syslog/caching"
	"github.com/cloudfoundry-community/firehose-to-syslog/events"
	"github.com/cloudfoundry-community/firehose-to-syslog/firehose"
	"github.com/cloudfoundry-community/firehose-to-syslog/logging"
	"github.com/cloudfoundry-community/go-cfclient"
	"gopkg.in/alecthomas/kingpin.v1"
	"log"
	"os"
	"time"
)

var (
	debug             = kingpin.Flag("debug", "Enable debug mode. This disables forwarding to syslog").Bool()
	domain            = kingpin.Flag("domain", "Domain of your CF installation.").Default("10.244.0.34.xip.io").String()
	syslogServer      = kingpin.Flag("syslog-server", "Syslog server.").String()
	subscriptionId    = kingpin.Flag("subscription-id", "Id for the subscription.").Default("firehose").String()
	user              = kingpin.Flag("user", "Admin user.").Default("admin").String()
	password          = kingpin.Flag("password", "Admin password.").Default("admin").String()
	skipSSLValidation = kingpin.Flag("skip-ssl-validation", "Please don't").Bool()
	wantedEvents      = kingpin.Flag("events", fmt.Sprintf("Comma seperated list of events you would like. Valid options are %s", events.GetListAuthorizedEventEvents())).Default("LogMessage").String()
	boldDatabasePath  = kingpin.Flag("boltdb-path", "Bolt Database path ").Default("my.db").String()
	tickerTime        = kingpin.Flag("cc-pull-time", "CloudController Pooling time in sec").Default("60s").Duration()
)

func main() {
	kingpin.Version("0.0.2 - ba541ca")
	kingpin.Parse()

	apiEndpoint := fmt.Sprintf("https://api.%s", *domain)
	uaaEndpoint := fmt.Sprintf("https://uaa.%s", *domain)
	dopplerEndpoint := fmt.Sprintf("wss://doppler.%s", *domain)

	c := cfclient.Config{
		ApiAddress:        apiEndpoint,
		LoginAddress:      uaaEndpoint,
		Username:          *user,
		Password:          *password,
		SkipSslValidation: *skipSSLValidation,
	}
	cfClient := cfclient.NewClient(&c)

	//Use bolt for in-memory  - file caching
	db, err := bolt.Open(*boldDatabasePath, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		log.Fatal("Error opening bolt db: ", err)
		os.Exit(1)

	}
	defer db.Close()
	caching.SetCfClient(cfClient)
	caching.SetAppDb(db)
	caching.CreateBucket()

	// Ticker Pooling the CC every X sec
	ccPooling := time.NewTicker(*tickerTime)

	go func() {
		for range ccPooling.C {
			caching.GetAllApp()

		}

	}()

	//Let's Update the database the first time
	log.Println("Staring filling app/space/org cache.")
	caching.GetAllApp()
	log.Println("Done filling cache, I will now start processing events!")

	token := cfClient.GetToken()
	firehose := firehose.CreateFirehoseChan(dopplerEndpoint, token, *subscriptionId, *skipSSLValidation)

	logging.SetupLogging(*syslogServer, *debug)

	selectedEvents := events.GetSelectedEvents(*wantedEvents)
	events.RouteEvents(firehose, selectedEvents)
}
<commit_msg>Update version.<commit_after>package main

import (
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/cloudfoundry-community/firehose-to-syslog/caching"
	"github.com/cloudfoundry-community/firehose-to-syslog/events"
	"github.com/cloudfoundry-community/firehose-to-syslog/firehose"
	"github.com/cloudfoundry-community/firehose-to-syslog/logging"
	"github.com/cloudfoundry-community/go-cfclient"
	"gopkg.in/alecthomas/kingpin.v1"
	"log"
	"os"
	"time"
)

var (
	debug             = kingpin.Flag("debug", "Enable debug mode. This disables forwarding to syslog").Bool()
	domain            = kingpin.Flag("domain", "Domain of your CF installation.").Default("10.244.0.34.xip.io").String()
	syslogServer      = kingpin.Flag("syslog-server", "Syslog server.").String()
	subscriptionId    = kingpin.Flag("subscription-id", "Id for the subscription.").Default("firehose").String()
	user              = kingpin.Flag("user", "Admin user.").Default("admin").String()
	password          = kingpin.Flag("password", "Admin password.").Default("admin").String()
	skipSSLValidation = kingpin.Flag("skip-ssl-validation", "Please don't").Bool()
	wantedEvents      = kingpin.Flag("events", fmt.Sprintf("Comma seperated list of events you would like. Valid options are %s", events.GetListAuthorizedEventEvents())).Default("LogMessage").String()
	boldDatabasePath  = kingpin.Flag("boltdb-path", "Bolt Database path ").Default("my.db").String()
	tickerTime        = kingpin.Flag("cc-pull-time", "CloudController Pooling time in sec").Default("60s").Duration()
)

func main() {
	kingpin.Version("0.1.0 - f3d31bd")
	kingpin.Parse()

	apiEndpoint := fmt.Sprintf("https://api.%s", *domain)
	uaaEndpoint := fmt.Sprintf("https://uaa.%s", *domain)
	dopplerEndpoint := fmt.Sprintf("wss://doppler.%s", *domain)

	c := cfclient.Config{
		ApiAddress:        apiEndpoint,
		LoginAddress:      uaaEndpoint,
		Username:          *user,
		Password:          *password,
		SkipSslValidation: *skipSSLValidation,
	}
	cfClient := cfclient.NewClient(&c)

	//Use bolt for in-memory  - file caching
	db, err := bolt.Open(*boldDatabasePath, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		log.Fatal("Error opening bolt db: ", err)
		os.Exit(1)

	}
	defer db.Close()
	caching.SetCfClient(cfClient)
	caching.SetAppDb(db)
	caching.CreateBucket()

	// Ticker Pooling the CC every X sec
	ccPooling := time.NewTicker(*tickerTime)

	go func() {
		for range ccPooling.C {
			caching.GetAllApp()

		}

	}()

	//Let's Update the database the first time
	log.Println("Staring filling app/space/org cache.")
	caching.GetAllApp()
	log.Println("Done filling cache, I will now start processing events!")

	token := cfClient.GetToken()
	firehose := firehose.CreateFirehoseChan(dopplerEndpoint, token, *subscriptionId, *skipSSLValidation)

	logging.SetupLogging(*syslogServer, *debug)

	selectedEvents := events.GetSelectedEvents(*wantedEvents)
	events.RouteEvents(firehose, selectedEvents)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"time"

	"net/http"
	_ "net/http/pprof"

	"github.com/jessevdk/go-flags"
	"github.com/moncho/dry/app"
	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
	"github.com/moncho/dry/version"
	"github.com/nsf/termbox-go"
	pkgError "github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

const (
	banner = `     _
    | |
  __| |  ____  _   _
 / _  | / ___)| | | |
( (_| || |    | |_| |
 \____||_|     \__  |
               (____/

`
	cheese     = "<white>made with ♥ (and go) by</> <blue>moncho</>"
	connecting = "ŏ Trying to connect to the Docker Host ŏ"
)

var loadMessage = []string{docker.Whale0,
	docker.Whale1,
	docker.Whale2,
	docker.Whale3,
	docker.Whale4,
	docker.Whale5,
	docker.Whale6,
	docker.Whale7,
	docker.Whale}

//dryOptions represents command line flags variables
type dryOptions struct {
	Description bool `short:"d" long:"description" description:"Dry description"`
	MonitorMode bool `short:"m" long:"monitor" description:"Starts dry in monitor mode"`
	// enable profiling
	Profile bool `short:"p" long:"profile" description:"Enable profiling"`
	Version bool `short:"v" long:"version" description:"Dry version"`
	//Docker-related properties
	DockerHost       string `short:"H" long:"docker_host" description:"Docker Host"`
	DockerCertPath   string `short:"c" long:"docker_certpath" description:"Docker cert path"`
	DockerTLSVerifiy string `short:"t" long:"docker_tls" description:"Docker TLS verify"`
}

//-----------------------------------------------------------------------------

func newApp(screen *ui.Screen, dockerEnv *docker.Env) (*app.Dry, error) {
	return app.NewDry(screen, dockerEnv)
}

func newDockerEnv(opts dryOptions) *docker.Env {
	dockerEnv := docker.NewEnv()
	if opts.DockerHost == "" {
		if os.Getenv("DOCKER_HOST") == "" {
			log.Info(
				fmt.Sprintf(
					"No DOCKER_HOST environment variable found and no Host parameter was given, trying %s",
					docker.DefaultDockerHost))
			dockerEnv.DockerHost = docker.DefaultDockerHost
		} else {
			dockerEnv.DockerHost = os.Getenv("DOCKER_HOST")
			dockerEnv.DockerTLSVerify = docker.GetBool(os.Getenv("DOCKER_TLS_VERIFY"))
			dockerEnv.DockerCertPath = os.Getenv("DOCKER_CERT_PATH")
		}
	} else {
		dockerEnv.DockerHost = opts.DockerHost
		dockerEnv.DockerTLSVerify = docker.GetBool(opts.DockerTLSVerifiy)
		dockerEnv.DockerCertPath = opts.DockerCertPath
	}
	return dockerEnv
}

func showLoadingScreen(screen *ui.Screen, dockerEnv *docker.Env, stop <-chan struct{}) {
	screen.Clear()
	midscreen := ui.ActiveScreen.Dimensions.Width / 2
	height := ui.ActiveScreen.Dimensions.Height
	screen.RenderAtColumn(midscreen-len(connecting)/2, 1, ui.White(connecting))
	screen.RenderLine(2, height-2, fmt.Sprintf("<blue>Dry Version:</> %s", ui.White(version.VERSION)))
	if dockerEnv != nil {
		screen.RenderLine(2, height-1, fmt.Sprintf("<blue>Docker Host:</> %s", ui.White(dockerEnv.DockerHost)))
	} else {
		screen.RenderLine(2, height-1, ui.White("No Docker host"))
	}

	//20 is a safe aproximation for the length of interpreted characters from the message
	screen.RenderLine(ui.ActiveScreen.Dimensions.Width-len(cheese)+20, height-1, cheese)
	screen.Flush()
	go func() {
		rotorPos := 0
		forward := true
		ticker := time.NewTicker(250 * time.Millisecond)
		timeOut := time.NewTimer(30 * time.Second)

		for {
			select {
			case <-ticker.C:
				loadingMessage := loadMessage[rotorPos]
				screen.RenderAtColumn(midscreen-19, height/2-6, ui.Cyan(loadingMessage))
				screen.Flush()
				if rotorPos == len(loadMessage)-1 {
					forward = false
				} else if rotorPos == 0 {
					forward = true
				}
				if forward {
					rotorPos++
				} else {
					rotorPos--
				}

			case <-timeOut.C:
				screen.Close()
				log.Error(
					"Dry could not connect with the host after 30 seconds.")
				os.Exit(0)
			case <-stop:
				return
			}
		}
	}()
}
func main() {
	running := false

	defer func() {
		if r := recover(); r != nil {
			termbox.Close()
			log.WithField("error", r).Error(
				"Dry panicked")
			log.Info("Bye")
			os.Exit(1)
		} else if running {
			log.Info("Bye")
		}
	}()
	// parse flags
	var opts dryOptions
	var parser = flags.NewParser(&opts, flags.Default)
	_, err := parser.Parse()
	if err != nil {
		flagError := err.(*flags.Error)
		if flagError.Type == flags.ErrHelp {
			return
		}
		if flagError.Type == flags.ErrUnknownFlag {
			log.Error("Use --help to view all available options.")
			return
		}
		log.Errorf("Error parsing flags: %s\n", err)
		return
	}
	if opts.Description {
		fmt.Print(app.ShortHelp)
		return
	}
	if opts.Version {
		fmt.Printf("dry version %s, build %s\n", version.VERSION, version.GITCOMMIT)
		return
	}
	log.Info("Launching dry")
	if err = termbox.Init(); err != nil {
		log.Error(pkgError.Wrap(err, "There was an error initializing termbox"))
		return
	}
	dockerEnv := newDockerEnv(opts)

	// Start profiling (if required)
	if opts.Profile {
		go func() {
			log.Info(http.ListenAndServe("localhost:6060", nil))
		}()
	}
	screen, err := ui.NewScreen(appui.DryTheme)
	if err != nil {
		log.WithField("error", err).Error(
			"There was an error launching dry")
		return
	}

	running = true

	//Loading screen
	stopLoadScreen := make(chan struct{}, 1)
	showLoadingScreen(screen, dockerEnv, stopLoadScreen)

	//newApp will load dry and try to establish a connection with the docker daemon
	dry, err := newApp(screen, dockerEnv)
	//dry has loaded, stop showing the loading screen
	close(stopLoadScreen)
	if opts.MonitorMode {
		dry.SetViewMode(app.Monitor)
	}
	if err == nil {
		app.RenderLoop(dry, screen)
		dry.Close()
		screen.Close()
	} else {
		//screen has to be closed before logging
		screen.Close()
		log.WithField("error", err).Error(
			"There was an error launching dry")
		return
	}
}
<commit_msg>Clean code<commit_after>package main

import (
	"fmt"
	"os"
	"time"

	"net/http"
	_ "net/http/pprof"

	"github.com/jessevdk/go-flags"
	"github.com/moncho/dry/app"
	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
	"github.com/moncho/dry/version"
	"github.com/nsf/termbox-go"
	pkgError "github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

const (
	banner = `     _
    | |
  __| |  ____  _   _
 / _  | / ___)| | | |
( (_| || |    | |_| |
 \____||_|     \__  |
               (____/

`
	cheese     = "<white>made with ♥ (and go) by</> <blue>moncho</>"
	connecting = "ŏ Trying to connect to the Docker Host ŏ"
)

var loadMessage = []string{docker.Whale0,
	docker.Whale1,
	docker.Whale2,
	docker.Whale3,
	docker.Whale4,
	docker.Whale5,
	docker.Whale6,
	docker.Whale7,
	docker.Whale}

//dryOptions represents command line flags variables
type dryOptions struct {
	Description bool `short:"d" long:"description" description:"Dry description"`
	MonitorMode bool `short:"m" long:"monitor" description:"Starts dry in monitor mode"`
	// enable profiling
	Profile bool `short:"p" long:"profile" description:"Enable profiling"`
	Version bool `short:"v" long:"version" description:"Dry version"`
	//Docker-related properties
	DockerHost       string `short:"H" long:"docker_host" description:"Docker Host"`
	DockerCertPath   string `short:"c" long:"docker_certpath" description:"Docker cert path"`
	DockerTLSVerifiy string `short:"t" long:"docker_tls" description:"Docker TLS verify"`
}

//-----------------------------------------------------------------------------

func newApp(screen *ui.Screen, dockerEnv *docker.Env) (*app.Dry, error) {
	return app.NewDry(screen, dockerEnv)
}

func newDockerEnv(opts dryOptions) *docker.Env {
	dockerEnv := docker.NewEnv()
	if opts.DockerHost == "" {
		if os.Getenv("DOCKER_HOST") == "" {
			log.Info(
				fmt.Sprintf(
					"No DOCKER_HOST environment variable found and no Host parameter was given, trying %s",
					docker.DefaultDockerHost))
			dockerEnv.DockerHost = docker.DefaultDockerHost
		} else {
			dockerEnv.DockerHost = os.Getenv("DOCKER_HOST")
			dockerEnv.DockerTLSVerify = docker.GetBool(os.Getenv("DOCKER_TLS_VERIFY"))
			dockerEnv.DockerCertPath = os.Getenv("DOCKER_CERT_PATH")
		}
	} else {
		dockerEnv.DockerHost = opts.DockerHost
		dockerEnv.DockerTLSVerify = docker.GetBool(opts.DockerTLSVerifiy)
		dockerEnv.DockerCertPath = opts.DockerCertPath
	}
	return dockerEnv
}

func showLoadingScreen(screen *ui.Screen, dockerEnv *docker.Env, stop <-chan struct{}) {
	screen.Clear()
	midscreen := ui.ActiveScreen.Dimensions.Width / 2
	height := ui.ActiveScreen.Dimensions.Height
	screen.RenderAtColumn(midscreen-len(connecting)/2, 1, ui.White(connecting))
	screen.RenderLine(2, height-2, fmt.Sprintf("<blue>Dry Version:</> %s", ui.White(version.VERSION)))
	if dockerEnv != nil {
		screen.RenderLine(2, height-1, fmt.Sprintf("<blue>Docker Host:</> %s", ui.White(dockerEnv.DockerHost)))
	} else {
		screen.RenderLine(2, height-1, ui.White("No Docker host"))
	}

	//20 is a safe aproximation for the length of interpreted characters from the message
	screen.RenderLine(ui.ActiveScreen.Dimensions.Width-len(cheese)+20, height-1, cheese)
	screen.Flush()
	go func() {
		rotorPos := 0
		forward := true
		ticker := time.NewTicker(250 * time.Millisecond)
		timeOut := time.NewTimer(30 * time.Second)

		for {
			select {
			case <-ticker.C:
				loadingMessage := loadMessage[rotorPos]
				screen.RenderAtColumn(midscreen-19, height/2-6, ui.Cyan(loadingMessage))
				screen.Flush()
				if rotorPos == len(loadMessage)-1 {
					forward = false
				} else if rotorPos == 0 {
					forward = true
				}
				if forward {
					rotorPos++
				} else {
					rotorPos--
				}

			case <-timeOut.C:
				screen.Close()
				log.Error(
					"Dry could not connect with the host after 30 seconds.")
				os.Exit(0)
			case <-stop:
				return
			}
		}
	}()
}
func main() {
	running := false

	defer func() {
		if r := recover(); r != nil {
			termbox.Close()
			log.WithField("error", r).Error(
				"Dry panicked")
			log.Info("Bye")
			os.Exit(1)
		} else if running {
			log.Info("Bye")
		}
	}()
	// parse flags
	var opts dryOptions
	var parser = flags.NewParser(&opts, flags.Default)
	_, err := parser.Parse()
	if err != nil {
		flagError := err.(*flags.Error)
		if flagError.Type == flags.ErrHelp {
			return
		}
		if flagError.Type == flags.ErrUnknownFlag {
			log.Error("Use --help to view all available options.")
			return
		}
		log.Errorf("Error parsing flags: %s\n", err)
		return
	}
	if opts.Description {
		fmt.Print(app.ShortHelp)
		return
	}
	if opts.Version {
		fmt.Printf("dry version %s, build %s\n", version.VERSION, version.GITCOMMIT)
		return
	}
	log.Info("Launching dry")
	if err = termbox.Init(); err != nil {
		log.Error(pkgError.Wrap(err, "There was an error initializing termbox"))
		return
	}
	dockerEnv := newDockerEnv(opts)

	// Start profiling (if required)
	if opts.Profile {
		go func() {
			log.Info(http.ListenAndServe("localhost:6060", nil))
		}()
	}
	screen, err := ui.NewScreen(appui.DryTheme)
	defer screen.Close()
	if err != nil {
		log.WithField("error", err).Error(
			"There was an error launching dry")
		return
	}

	running = true

	//Loading screen
	stopLoadScreen := make(chan struct{})
	showLoadingScreen(screen, dockerEnv, stopLoadScreen)

	//newApp will load dry and try to establish a connection with the docker daemon
	dry, err := newApp(screen, dockerEnv)
	//dry has loaded, stop showing the loading screen
	close(stopLoadScreen)

	if err == nil {
		if opts.MonitorMode {
			dry.SetViewMode(app.Monitor)
		}
		app.RenderLoop(dry, screen)
		dry.Close()
	} else {
		//screen has to be closed before logging
		log.WithField("error", err).Error(
			"There was an error launching dry")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/doozr/goslack"
	"github.com/doozr/qbot/command"
	"github.com/doozr/qbot/dispatch"
	"github.com/doozr/qbot/notification"
	"github.com/doozr/qbot/queue"
	"github.com/doozr/qbot/usercache"
	"github.com/doozr/qbot/util"
)

// Version is the current release version
const Version = "1.1"

func listen(name string, connection *goslack.Connection, messageChan dispatch.MessageChan, userChan dispatch.UserChan) {

	for {
		// read each incoming message
		e := <-connection.RealTime

		// see if we're mentioned
		if e.Type == "message" {
			m, err := e.RtmMessage()
			if err != nil {
				log.Println(err)
				continue
			}

			directedAtUs := strings.HasPrefix(m.Text, name) || strings.HasPrefix(m.Text, "<@"+connection.ID+">")
			if directedAtUs {
				_, m.Text = util.StringPop(m.Text)
				messageChan <- m
			} else if util.IsPrivateChannel(m.Channel) {
				messageChan <- m
			}
		}

		// see if it's a user update
		if e.Type == "user_change" {
			uc, err := e.RtmUserChange()
			if err != nil {
				log.Println(err)
				continue
			}
			userChan <- uc.User
		}
	}
}

func main() {
	if len(os.Args) < 3 {
		fmt.Println("Usage: qbot <token> <data file>")
		os.Exit(1)
	}

	log.Printf("Qbot version %s", Version)

	// Get command line parameters
	token := os.Args[1]
	filename := os.Args[2]

	// Instantiate state
	connection := connectToSlack(token)
	userCache := getUserList(connection)
	name := getBotName(userCache, connection.ID)
	q := loadQueue(filename)

	// Set up command and response processors
	notifications := notification.New(userCache)
	commands := command.New(notifications, userCache)

	// Create channels
	messageChan := make(dispatch.MessageChan, 100)
	saveChan := make(dispatch.SaveChan, 5)
	notifyChan := make(dispatch.NotifyChan, 5)
	userChan := make(dispatch.UserChan, 5)

	// Start goroutines
	go dispatch.Message(name, q, commands, messageChan, saveChan, notifyChan)
	go dispatch.Save(filename, saveChan)
	go dispatch.Notify(connection, notifyChan)
	go dispatch.User(userCache, userChan)

	// Dispatch incoming events
	log.Println("Ready to receive events")
	listen(name, connection, messageChan, userChan)
}

func connectToSlack(token string) (connection *goslack.Connection) {
	log.Print("Connecting to Slack")
	connection, err := goslack.New(token)
	if err != nil {
		log.Fatal(err)
	}
	return
}

func getUserList(connection *goslack.Connection) (userCache *usercache.UserCache) {
	log.Println("Getting user list")
	users, err := connection.GetUserList()
	if err != nil {
		log.Fatal(err)
	}
	userCache = usercache.New(users)
	return
}

func getBotName(userCache *usercache.UserCache, id string) (name string) {
	name = userCache.GetUserName(id)
	if name == "" {
		log.Fatal("Could not get username of bot")
	}
	log.Printf("Responding to requests directed to @%s", name)
	return
}

func loadQueue(filename string) (q queue.Queue) {
	log.Printf("Attempting to load queue from %s", filename)
	q, err := queue.Load(filename)
	if err != nil {
		log.Fatalf("Error loading queue: %s", err)
	}
	return
}
<commit_msg>Bump to v1.2<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/doozr/goslack"
	"github.com/doozr/qbot/command"
	"github.com/doozr/qbot/dispatch"
	"github.com/doozr/qbot/notification"
	"github.com/doozr/qbot/queue"
	"github.com/doozr/qbot/usercache"
	"github.com/doozr/qbot/util"
)

// Version is the current release version
const Version = "1.2"

func listen(name string, connection *goslack.Connection, messageChan dispatch.MessageChan, userChan dispatch.UserChan) {

	for {
		// read each incoming message
		e := <-connection.RealTime

		// see if we're mentioned
		if e.Type == "message" {
			m, err := e.RtmMessage()
			if err != nil {
				log.Println(err)
				continue
			}

			directedAtUs := strings.HasPrefix(m.Text, name) || strings.HasPrefix(m.Text, "<@"+connection.ID+">")
			if directedAtUs {
				_, m.Text = util.StringPop(m.Text)
				messageChan <- m
			} else if util.IsPrivateChannel(m.Channel) {
				messageChan <- m
			}
		}

		// see if it's a user update
		if e.Type == "user_change" {
			uc, err := e.RtmUserChange()
			if err != nil {
				log.Println(err)
				continue
			}
			userChan <- uc.User
		}
	}
}

func main() {
	if len(os.Args) < 3 {
		fmt.Println("Usage: qbot <token> <data file>")
		os.Exit(1)
	}

	log.Printf("Qbot version %s", Version)

	// Get command line parameters
	token := os.Args[1]
	filename := os.Args[2]

	// Instantiate state
	connection := connectToSlack(token)
	userCache := getUserList(connection)
	name := getBotName(userCache, connection.ID)
	q := loadQueue(filename)

	// Set up command and response processors
	notifications := notification.New(userCache)
	commands := command.New(notifications, userCache)

	// Create channels
	messageChan := make(dispatch.MessageChan, 100)
	saveChan := make(dispatch.SaveChan, 5)
	notifyChan := make(dispatch.NotifyChan, 5)
	userChan := make(dispatch.UserChan, 5)

	// Start goroutines
	go dispatch.Message(name, q, commands, messageChan, saveChan, notifyChan)
	go dispatch.Save(filename, saveChan)
	go dispatch.Notify(connection, notifyChan)
	go dispatch.User(userCache, userChan)

	// Dispatch incoming events
	log.Println("Ready to receive events")
	listen(name, connection, messageChan, userChan)
}

func connectToSlack(token string) (connection *goslack.Connection) {
	log.Print("Connecting to Slack")
	connection, err := goslack.New(token)
	if err != nil {
		log.Fatal(err)
	}
	return
}

func getUserList(connection *goslack.Connection) (userCache *usercache.UserCache) {
	log.Println("Getting user list")
	users, err := connection.GetUserList()
	if err != nil {
		log.Fatal(err)
	}
	userCache = usercache.New(users)
	return
}

func getBotName(userCache *usercache.UserCache, id string) (name string) {
	name = userCache.GetUserName(id)
	if name == "" {
		log.Fatal("Could not get username of bot")
	}
	log.Printf("Responding to requests directed to @%s", name)
	return
}

func loadQueue(filename string) (q queue.Queue) {
	log.Printf("Attempting to load queue from %s", filename)
	q, err := queue.Load(filename)
	if err != nil {
		log.Fatalf("Error loading queue: %s", err)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	//"net"
	"strconv"
	flags "github.com/jessevdk/go-flags"
	"net"
	"log"
	"bufio"
	"bytes"
	"strings"
	"os"
	"fmt"
	"regexp"
	"runtime"
	"errors"
)

const VERSION = "0.0.1"
var matcher = regexp.MustCompile(`^(<(\d+)>)?(.*)`)

type SyslogMsg struct {
	Priority int
	Facility int
	Severity int
	Msg		 string
}

func chanByteReader(ch <-chan *SyslogMsg) {
	for m := range ch {
		fmt.Printf("<%d> %s\n", m.Priority, m.Msg)
	}
}

func parseSyslogMsg(buf []byte) (*SyslogMsg, error) {
	matches := matcher.FindSubmatch(buf)
	if len(matches) == 0 {
		return nil, errors.New("No match")
	}

	var prio int
	var err error
	if len(matches[2]) != 0 {
		prio, err = strconv.Atoi(string(matches[2]))
		if err != nil {
			return nil, errors.New("prio failed to convert")
		}
	} else {
		// default prio a relay must write if none is readable
		prio = 13
	}

	m := &SyslogMsg{
		Priority: prio,
		Facility: prio/8,
		Severity: prio % 8}
	m.Msg = string(bytes.Trim(matches[3], "\n"))
	return m, nil
}

func udpAcceptor(pc *net.UDPConn, ch chan<- *SyslogMsg) {
	buf := make([]byte, 1024)
	for {
		n, _, err := pc.ReadFromUDP(buf)
		if err != nil {
			log.Println(err)
			continue
		}
		fmt.Printf("read %d\n", n)
		if n == 0 {
			continue
		}
		m, err := parseSyslogMsg(buf[0:n])
		if err != nil {
			continue
		}
		ch <- m
	}
}

func handleConn(c net.Conn, ch chan<- *SyslogMsg) {
	bufc := bufio.NewReader(c)
	defer c.Close()

	pk, err := bufc.Peek(1)
	if err != nil {
		return
	}
	if !bytes.Equal(pk, []byte("<")) {
		for {
			s, err := bufc.ReadString(' ')
			if err != nil {
				log.Println(err)
				break
			}
			s = strings.Trim(s, " ")
			readsize, err := strconv.Atoi(s)
			if err != nil {
				log.Println(err)
				break
			}

			var result []byte
			for i := 0; i < readsize; i++ {
				b, err := bufc.ReadByte()
				if err != nil {
					log.Println(err)
					break
				}
				result = append(result, b)
			}
			if len(result) > 0 {
				m, err := parseSyslogMsg(result)
				if err != nil {
					continue
				}
				ch <- m
			}
		}
	} else {
		for {
			result, err := bufc.ReadBytes('\n')
			if err != nil {
				log.Println(err)
				break
			}
			result = bytes.Trim(result, " ")
			if len(result) > 0 {
				m, err := parseSyslogMsg(result)
				if err != nil {
					log.Println(err)
					continue
				}
				ch <- m
			}
		}
	}
}

func tcpAcceptor(ln net.Listener, ch chan<- *SyslogMsg) {
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		log.Println("got conn")
		go handleConn(conn, ch)
	}
}

func main() {
	var gmx int
	if gmxEnv := os.Getenv("GOMAXPROCS"); gmxEnv != "" {
		gmx, _ = strconv.Atoi(gmxEnv)
	} else {
		gmx = runtime.NumCPU()
	}
	runtime.GOMAXPROCS(gmx)

	// command line flags
	var opts struct {
		BindTCP string `long:"listen-tcp" default:"" description:"TCP address:port to listen to"`
		BindUDP string `long:"listen-udp" default:"" description:"UDP address:port to listen to"`
		Verbose bool   `short:"v" long:"verbose" description:"Show verbose (debug) log level output"`
		Version bool   `short:"V" long:"version" description:"print version and exit"`
	}

	// parse said flags
	_, err := flags.Parse(&opts)
	if err != nil {
		if e, ok := err.(*flags.Error); ok {
			if e.Type == flags.ErrHelp {
				os.Exit(0)
			}
		}
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf("slurpy-%s (%s,%s-%s)\n", VERSION, runtime.Version(), runtime.Compiler, runtime.GOARCH)
		os.Exit(0)
	}

	if opts.BindTCP == "" && opts.BindUDP == "" {
		fmt.Println("No listen ports defined. Exiting.")
		os.Exit(1)
	}

	ch := make(chan *SyslogMsg, 1024)
	go chanByteReader(ch)

	if opts.BindTCP != "" {
		ltcp, err := net.Listen("tcp", opts.BindTCP)
		if err != nil {
			log.Fatal(err)
		}
		defer ltcp.Close()
		go tcpAcceptor(ltcp, ch)
	}

	if opts.BindUDP != "" {
		addr, err := net.ResolveUDPAddr("udp", opts.BindUDP)
		if err != nil {
			log.Fatal(err)
		}
		ludp, err := net.ListenUDP("udp", addr)
		if err != nil {
			log.Fatal(err)
		}
		defer ludp.Close()
		go udpAcceptor(ludp, ch)
	}
	select {}
}
<commit_msg>go fmt<commit_after>package main

import (
	//"net"
	"bufio"
	"bytes"
	"errors"
	"fmt"
	flags "github.com/jessevdk/go-flags"
	"log"
	"net"
	"os"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

const VERSION = "0.0.1"

var matcher = regexp.MustCompile(`^(<(\d+)>)?(.*)`)

type SyslogMsg struct {
	Priority int
	Facility int
	Severity int
	Msg      string
}

func chanByteReader(ch <-chan *SyslogMsg) {
	for m := range ch {
		fmt.Printf("<%d> %s\n", m.Priority, m.Msg)
	}
}

func parseSyslogMsg(buf []byte) (*SyslogMsg, error) {
	matches := matcher.FindSubmatch(buf)
	if len(matches) == 0 {
		return nil, errors.New("No match")
	}

	var prio int
	var err error
	if len(matches[2]) != 0 {
		prio, err = strconv.Atoi(string(matches[2]))
		if err != nil {
			return nil, errors.New("prio failed to convert")
		}
	} else {
		// default prio a relay must write if none is readable
		prio = 13
	}

	m := &SyslogMsg{
		Priority: prio,
		Facility: prio / 8,
		Severity: prio % 8}
	m.Msg = string(bytes.Trim(matches[3], "\n"))
	return m, nil
}

func udpAcceptor(pc *net.UDPConn, ch chan<- *SyslogMsg) {
	buf := make([]byte, 1024)
	for {
		n, _, err := pc.ReadFromUDP(buf)
		if err != nil {
			log.Println(err)
			continue
		}
		fmt.Printf("read %d\n", n)
		if n == 0 {
			continue
		}
		m, err := parseSyslogMsg(buf[0:n])
		if err != nil {
			continue
		}
		ch <- m
	}
}

func handleConn(c net.Conn, ch chan<- *SyslogMsg) {
	bufc := bufio.NewReader(c)
	defer c.Close()

	pk, err := bufc.Peek(1)
	if err != nil {
		return
	}
	if !bytes.Equal(pk, []byte("<")) {
		for {
			s, err := bufc.ReadString(' ')
			if err != nil {
				log.Println(err)
				break
			}
			s = strings.Trim(s, " ")
			readsize, err := strconv.Atoi(s)
			if err != nil {
				log.Println(err)
				break
			}

			var result []byte
			for i := 0; i < readsize; i++ {
				b, err := bufc.ReadByte()
				if err != nil {
					log.Println(err)
					break
				}
				result = append(result, b)
			}
			if len(result) > 0 {
				m, err := parseSyslogMsg(result)
				if err != nil {
					continue
				}
				ch <- m
			}
		}
	} else {
		for {
			result, err := bufc.ReadBytes('\n')
			if err != nil {
				log.Println(err)
				break
			}
			result = bytes.Trim(result, " ")
			if len(result) > 0 {
				m, err := parseSyslogMsg(result)
				if err != nil {
					log.Println(err)
					continue
				}
				ch <- m
			}
		}
	}
}

func tcpAcceptor(ln net.Listener, ch chan<- *SyslogMsg) {
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		log.Println("got conn")
		go handleConn(conn, ch)
	}
}

func main() {
	var gmx int
	if gmxEnv := os.Getenv("GOMAXPROCS"); gmxEnv != "" {
		gmx, _ = strconv.Atoi(gmxEnv)
	} else {
		gmx = runtime.NumCPU()
	}
	runtime.GOMAXPROCS(gmx)

	// command line flags
	var opts struct {
		BindTCP string `long:"listen-tcp" default:"" description:"TCP address:port to listen to"`
		BindUDP string `long:"listen-udp" default:"" description:"UDP address:port to listen to"`
		Verbose bool   `short:"v" long:"verbose" description:"Show verbose (debug) log level output"`
		Version bool   `short:"V" long:"version" description:"print version and exit"`
	}

	// parse said flags
	_, err := flags.Parse(&opts)
	if err != nil {
		if e, ok := err.(*flags.Error); ok {
			if e.Type == flags.ErrHelp {
				os.Exit(0)
			}
		}
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf("slurpy-%s (%s,%s-%s)\n", VERSION, runtime.Version(), runtime.Compiler, runtime.GOARCH)
		os.Exit(0)
	}

	if opts.BindTCP == "" && opts.BindUDP == "" {
		fmt.Println("No listen ports defined. Exiting.")
		os.Exit(1)
	}

	ch := make(chan *SyslogMsg, 1024)
	go chanByteReader(ch)

	if opts.BindTCP != "" {
		ltcp, err := net.Listen("tcp", opts.BindTCP)
		if err != nil {
			log.Fatal(err)
		}
		defer ltcp.Close()
		go tcpAcceptor(ltcp, ch)
	}

	if opts.BindUDP != "" {
		addr, err := net.ResolveUDPAddr("udp", opts.BindUDP)
		if err != nil {
			log.Fatal(err)
		}
		ludp, err := net.ListenUDP("udp", addr)
		if err != nil {
			log.Fatal(err)
		}
		defer ludp.Close()
		go udpAcceptor(ludp, ch)
	}
	select {}
}
<|endoftext|>


<commit_before>package main

/* TODO:
   * Consider interface for beacons, abstracting different versions
   * Serve webpage to make query, with js connection via websocket to receive responses
   * Make sure non-exported stuff (that isn't needed for JSON) is lowercase
   * Automated tests
   * Documentation
   * Dockerfile
   * Integrate OpenID connect
     * https://github.com/coreos/go-oidc
     * Read IDP configurations from config files, fetch keys, etc.
     * Display interfaces for logging in with various IDPs
     * Send auth headers to beacons along with queries
*/

import (
	"fmt"
	"net/http"

	// "github.com/knoxcarey/bob/idp"
	"github.com/knoxcarey/bob/beacon"
)

// Handle HTTP query
func queryHandler(w http.ResponseWriter, r *http.Request) {
	query := beacon.BeaconQuery(r.URL.Query())
	results := beacon.QueryBeaconsSync(query, timeout)
	w.Header().Set("Content-Type", "application/json")
	w.Write(results)
}

// Entry point
func main() {
	fmt.Printf("BoB is listening on port %d\n", port)
	http.HandleFunc("/", queryHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
<commit_msg>Documentation/todo update<commit_after>package main

/* TODO:
   * Serve webpage to make query, with js connection via websocket to receive responses
   * Extract common code from beacon versions
   * Automated tests
   * Documentation
   * Dockerfile
   * Integrate OpenID connect
     * https://github.com/coreos/go-oidc
     * Read IDP configurations from config files, fetch keys, etc.
     * Display interfaces for logging in with various IDPs
     * Send auth headers to beacons along with queries
*/

import (
	"fmt"
	"net/http"

	// "github.com/knoxcarey/bob/idp"
	"github.com/knoxcarey/bob/beacon"
)

// Handle HTTP query
func queryHandler(w http.ResponseWriter, r *http.Request) {
	query := beacon.BeaconQuery(r.URL.Query())
	results := beacon.QueryBeaconsSync(query, timeout)
	w.Header().Set("Content-Type", "application/json")
	w.Write(results)
}

// Entry point
func main() {
	fmt.Printf("BoB is listening on port %d\n", port)
	http.HandleFunc("/", queryHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

var (
	port     = ":8000"
	commands = map[string]string{
		"ping":     `{"text": "PING FIVE! WHOO-PSH! SNAP!"}`,
		"betrayal": `{"text": "<https://www.youtube.com/watch?v=uw5P2up5bL4>"}`,
		"miracle":  `{"text": "<https://www.youtube.com/watch?v=uehf8e43Vtk>"}`,
		"meow":     `{"text": "<https://www.youtube.com/watch?v=xRhATB9NelU>"}`,
	}
	notFound = `{"text": "That command was bad and you should feel bad."}`
	hookURL  = os.Getenv("SLACK_WEBHOOK_URL")
)

func handleHook(res http.ResponseWriter, req *http.Request, val string) {
	client := &http.Client{}
	req, err := http.NewRequest("POST", hookURL, bytes.NewBuffer([]byte(val)))
	req.Header.Add("Content-Type", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Status:", resp.Status)
}

func handleQuery(res http.ResponseWriter, req *http.Request) {
	res.Header().Set("Content-Type", "application/json")
	qMap := req.URL.Query()
	qVal := qMap["q"][0]
	if commands[qVal] != "" {
		handleHook(res, req, commands[qVal])
		io.WriteString(res, `{"status": "Valid Command"}`)
	} else {
		handleHook(res, req, notFound)
		io.WriteString(res, `{"status": "Invalid Command"}`)
	}
}

func main() {
	fmt.Println("Server running listening on port", port)
	http.HandleFunc("/command", handleQuery)
	http.ListenAndServe(port, nil)
}
<commit_msg>Let's add some tests.<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)

type slackMsg struct {
	Text  string
	Token string
}

var (
	port     = ":8000"
	commands = map[string]string{
		"ping":     `{"text": "PING FIVE! WHOO-PSH! SNAP!"}`,
		"betrayal": `{"text": "<https://www.youtube.com/watch?v=uw5P2up5bL4>"}`,
		"miracle":  `{"text": "<https://www.youtube.com/watch?v=uehf8e43Vtk>"}`,
		"meow":     `{"text": "<https://www.youtube.com/watch?v=xRhATB9NelU>"}`,
	}
	notFound     = `{"text": "That command was bad and you should feel bad. #ZoidbergLevelRoast"}`
	hookURL      = os.Getenv("SLACK_WEBHOOK_URL")
	slackToken   = os.Getenv("SLACK_TOKEN")
	trigger      = "thetodd: "
	invalidToken = `{"status": "Invalid Token"}`
	notAllowed   = `{"status": "Method Not Allowed"}`
)

func hello(res http.ResponseWriter, req *http.Request) {
	res.Header().Set("Contet-Type", "application/json")
	io.WriteString(res, `{"status": "Sup, fam?"}`)
}

func handlePost(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		err := r.ParseForm()
		if err != nil {
			log.Fatal(err)
		}
		command := strings.TrimPrefix(r.Form.Get("text"), trigger)
		msg := slackMsg{command, r.Form.Get("token")}
		if msg.Token == slackToken {
			if commands[msg.Text] != "" {
				io.WriteString(w, commands[msg.Text])
			} else {
				io.WriteString(w, notFound)
			}
		} else {
			io.WriteString(w, invalidToken)
		}
	} else {
		io.WriteString(w, notAllowed)
	}
}

func main() {
	fmt.Println("Server running listening on port", port)
	http.HandleFunc("/command", handlePost)
	http.HandleFunc("/hello", hello)
	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/jteeuwen/ircb/cmd"
	"github.com/jteeuwen/ircb/net"
	"github.com/jteeuwen/ircb/plugin"
	"github.com/jteeuwen/ircb/proto"
	"log"
	"os"
	"path/filepath"

	_ "github.com/ChimeraCoder/gopherbot/plugins/url"
	_ "github.com/jteeuwen/ircb/plugins/admin"
	_ "github.com/jteeuwen/ircb/plugins/dict"
	_ "github.com/ChimeraCoder/gopherbot/plugins/reputation"
)

func main() {
	conn, client := setup()
	defer shutdown(conn, client)

	// Perform handshake.
	log.Printf("Performing handshake...")
	client.User(config.Nickname)
	client.Nick(config.Nickname, config.NickservPassword)

	// Main data loop.
	log.Printf("Entering data loop...")
	for {
		line, err := conn.ReadLine()

		if err != nil {
			break
		}

		client.Read(string(line))
	}
}

// setup initializes the application.
func setup() (*net.Conn, *proto.Client) {
	// parse commandline arguments and create configuration.
	config = parseArgs()

	log.Printf("Connecting to %s...", config.Address)

	// Open connection to server.
	conn, err := net.Dial(config.Address, config.SSLCert, config.SSLKey)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Dial: %v\n", err)
		os.Exit(1)
	}

	log.Println("Connection established.")

	// Create client protocol.
	client := proto.NewClient(func(p []byte) error {
		_, err := conn.Write(p)
		return err
	})

	// Initialize plugins.
	err = plugin.Load(config.Profile, client)
	if err != nil {
		log.Fatal(err)
	}

	// Inform command package of our user whitelist.
	cmd.SetWhitelist(config.Whitelist)

	// Bind protocol handlers and commands.
	bind(client)

	return conn, client
}

// shutdown cleans up our mess.
func shutdown(conn *net.Conn, client *proto.Client) {
	plugin.Unload(client)

	log.Printf("Shutting down.")
	client.Quit(config.QuitMessage)
	client.Close()
	conn.Close()
}

// parseArgs reads and verfies commandline arguments.
// It loads and returns a configuration object.
func parseArgs() *Config {
	profile := flag.String("p", "", "Path to bot profile directory.")
	version := flag.Bool("v", false, "Display version information.")

	flag.Parse()

	if *version {
		fmt.Printf("%s\n", Version())
		os.Exit(0)
	}

	if len(*profile) == 0 {
		fmt.Fprintf(os.Stderr, "Missing profile directory.\n")
		flag.Usage()
		os.Exit(1)
	}

	var c Config
	c.Profile = filepath.Clean(*profile)

	err := c.Load(filepath.Join(c.Profile, "config.ini"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Load config: %v\n", err)
		os.Exit(1)
	}

	return &c
}
<commit_msg>Run gofmt on all files<commit_after>// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/jteeuwen/ircb/cmd"
	"github.com/jteeuwen/ircb/net"
	"github.com/jteeuwen/ircb/plugin"
	"github.com/jteeuwen/ircb/proto"
	"log"
	"os"
	"path/filepath"

	_ "github.com/ChimeraCoder/gopherbot/plugins/reputation"
	_ "github.com/ChimeraCoder/gopherbot/plugins/url"
	_ "github.com/jteeuwen/ircb/plugins/admin"
	_ "github.com/jteeuwen/ircb/plugins/dict"
)

func main() {
	conn, client := setup()
	defer shutdown(conn, client)

	// Perform handshake.
	log.Printf("Performing handshake...")
	client.User(config.Nickname)
	client.Nick(config.Nickname, config.NickservPassword)

	// Main data loop.
	log.Printf("Entering data loop...")
	for {
		line, err := conn.ReadLine()

		if err != nil {
			break
		}

		client.Read(string(line))
	}
}

// setup initializes the application.
func setup() (*net.Conn, *proto.Client) {
	// parse commandline arguments and create configuration.
	config = parseArgs()

	log.Printf("Connecting to %s...", config.Address)

	// Open connection to server.
	conn, err := net.Dial(config.Address, config.SSLCert, config.SSLKey)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Dial: %v\n", err)
		os.Exit(1)
	}

	log.Println("Connection established.")

	// Create client protocol.
	client := proto.NewClient(func(p []byte) error {
		_, err := conn.Write(p)
		return err
	})

	// Initialize plugins.
	err = plugin.Load(config.Profile, client)
	if err != nil {
		log.Fatal(err)
	}

	// Inform command package of our user whitelist.
	cmd.SetWhitelist(config.Whitelist)

	// Bind protocol handlers and commands.
	bind(client)

	return conn, client
}

// shutdown cleans up our mess.
func shutdown(conn *net.Conn, client *proto.Client) {
	plugin.Unload(client)

	log.Printf("Shutting down.")
	client.Quit(config.QuitMessage)
	client.Close()
	conn.Close()
}

// parseArgs reads and verfies commandline arguments.
// It loads and returns a configuration object.
func parseArgs() *Config {
	profile := flag.String("p", "", "Path to bot profile directory.")
	version := flag.Bool("v", false, "Display version information.")

	flag.Parse()

	if *version {
		fmt.Printf("%s\n", Version())
		os.Exit(0)
	}

	if len(*profile) == 0 {
		fmt.Fprintf(os.Stderr, "Missing profile directory.\n")
		flag.Usage()
		os.Exit(1)
	}

	var c Config
	c.Profile = filepath.Clean(*profile)

	err := c.Load(filepath.Join(c.Profile, "config.ini"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Load config: %v\n", err)
		os.Exit(1)
	}

	return &c
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	log "github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/carbocation/interpose"
	gorilla_mux "github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/gorilla/mux"
	"github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/tylerb/graceful"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"
	"time"
)

// Define string constants that correspond
// to each supported action here
const (
	ActionWater   string = "water"
	ActionNothing string = "nothing"
)

// Variables for keeping track of the current vote count
// for each action.
// These should probably be stored in redis at some point.
var VoteCountWater int = 0
var VoteCountNothing int = 0

type Application struct {
}

type CurrentAction struct {
	Action        string `json:"action"`
	UnixTimestamp int64  `json:"unixTimestamp"`
}

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

	json.NewEncoder(w).Encode(lastExecutedAction)
}

// Number of seconds before votes are processed and reset
const votingTimePeriod int64 = 30

var lastExecutedAction = CurrentAction{
	Action:        ActionNothing,
	UnixTimestamp: 0,
}

func update() {

	var currentTime = int64(time.Now().Unix())

	if (currentTime - lastExecutedAction.UnixTimestamp) >= votingTimePeriod {
		// The voting time period is over, so update the current winning vote

		lastExecutedAction.Action = GetWinningAction()
		lastExecutedAction.UnixTimestamp = currentTime

		resetVoteCount()
	}
}

func resetVoteCount() {
	VoteCountWater = 0
	VoteCountNothing = 0
}

func GetWinningAction() string {
	// Return the action that has the greatest number of votes

	var winningAction string

	if VoteCountWater > VoteCountNothing {
		winningAction = ActionWater
	} else {
		winningAction = ActionNothing
	}

	return winningAction
}

func DebugPrintHttpRequest(r *http.Request) {
	// If debug logger is enabled,
	// print out all the details of the supplied HTTP request.
	if log.GetLevel() == log.DebugLevel {
		dump, err := httputil.DumpRequest(r, true)
		if err == nil {
			log.Debug("Request received: \n" + string(dump))
		} else {
			log.Debug("Error reading request: " + err.Error())
		}
	}
}

/*
Example cURL commands for invoking the API on the commandline:

curl -H "Content-Type: application/json" -X POST -d '{"someKey1":"someValue1","someKey2":"someValue2"}' http://127.0.0.1:5000/votes
curl -H "Content-Type: application/json" -X POST -d '{"action":"water"}' http://127.0.0.1:5000/votes
curl -H "Content-Type: application/json" -X POST -d '{"action":"nothing"}' http://127.0.0.1:5000/votes

curl http://127.0.0.1:5000/current_action
*/

type Vote struct {
	Action string `json:"action"`
}

type VoteReceipt struct {
	Message string `json:"message"`
	Vote    Vote   `json:"vote"`
}

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

	log.Debug("PostVotes called")

	DebugPrintHttpRequest(r)

	decoder := json.NewDecoder(r.Body)
	var vote Vote
	var message string
	err := decoder.Decode(&vote)

	if err == nil {

		if vote.Action == ActionWater {
			VoteCountWater++
			message = fmt.Sprintf("Voted for action \"%s\" total count is %d", ActionWater, VoteCountWater)
		} else if vote.Action == ActionNothing {
			VoteCountNothing++
			message = fmt.Sprintf("Voted for action \"%s\" Total Count: %d", ActionNothing, VoteCountNothing)
		} else {
			message = fmt.Sprintf("Encountered unhandled action \"%s\"", vote.Action)
			// TODO: return a standard error object
			w.WriteHeader(http.StatusBadRequest)
		}
	} else {
		message = "Error parsing vote body: " + err.Error()
		w.WriteHeader(http.StatusBadRequest)
	}

	log.Debug(message)

	json.NewEncoder(w).Encode(&VoteReceipt{
		Message: message,
		Vote:    vote,
	})
}

type ActionInfo struct {
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
	VoteCount   int    `json:"voteCount"`
}

type CurrentVoteInfo struct {
	Actions []ActionInfo `json:"actions"`
}

/*
Example response from GET /votes
{
  "actions": [
    {
      "name": "nothing",
      "displayName": "Nothing",
      "voteCount": 123
    },
    {
      "name": "water",
      "displayName": "Water",
      "voteCount": 47
    }
  ]
}
*/
func GetVotes(w http.ResponseWriter, r *http.Request) {

	update()

	currentVoteInfo := &CurrentVoteInfo{
		Actions: []ActionInfo{
			ActionInfo{Name: ActionNothing, DisplayName: "Nothing", VoteCount: VoteCountNothing},
			ActionInfo{Name: ActionWater, DisplayName: "Water", VoteCount: VoteCountWater},
		},
	}

	json.NewEncoder(w).Encode(currentVoteInfo)
}
func NewApplication() (*Application, error) {
	app := &Application{}
	return app, nil
}

type preFlightHandler func(http.ResponseWriter, *http.Request)

func NewPreFlightHandler(methods ...string) preFlightHandler {
	return func(w http.ResponseWriter, r *http.Request) {
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ", "))
			w.Header().Set("Access-Control-Allow-Headers", "content-type")
		}
	}
}

func (app *Application) mux() *gorilla_mux.Router {
	router := gorilla_mux.NewRouter()

	router.HandleFunc("/current_action", GetCurrentAction).Methods("GET")
	router.HandleFunc("/votes", PostVotes).Methods("POST")
	router.HandleFunc("/votes", GetVotes).Methods("GET")
	router.HandleFunc("/votes", NewPreFlightHandler("GET", "POST")).Methods("OPTIONS")

	return router
}

func InitLogLevel() {
	// Check if --debug argument was supplied on the command line.
	// Check if LIVEPLANTDEBUG environment variable is present.
	// (Environment variable takes precedence over command line flag)
	// Enable or disable debug logger accordingly.

	// Declare and parse command line flag
	boolPtr := flag.Bool("debug", false, "Whether or not to enable debug logger.")
	flag.Parse()

	var debugLoggerEnabled bool = *boolPtr

	if len(os.Getenv("LIVEPLANTDEBUG")) > 0 {
		// Environment variable is present, so
		// debug logger should be enabled.
		// (overrides command line flag)
		debugLoggerEnabled = true
	}

	if debugLoggerEnabled {
		// Log everything
		log.SetLevel(log.DebugLevel)
		log.Debug("Debug logging enabled")
	} else {
		// Only log fatal or panic events
		// (events where the application is terminated)
		log.SetLevel(log.FatalLevel)
	}
}

func main() {

	InitLogLevel()

	log.Println("Launching liveplant server")

	app, _ := NewApplication()
	middle := interpose.New()

	middle.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			w.Header().Set("Access-Control-Allow-Origin", "*")
			next.ServeHTTP(w, req)
		})
	})

	middle.UseHandler(app.mux())

	ServerPort := "5000" // default port
	if len(os.Getenv("PORT")) > 0 {
		ServerPort = os.Getenv("PORT")
	}
	drainInterval, _ := time.ParseDuration("1s")
	srv := &graceful.Server{
		Timeout: drainInterval,
		Server: &http.Server{
			Addr:    ":" + ServerPort,
			Handler: middle,
		},
	}

	log.Println("Running liveplant server on port " + ServerPort)

	err := srv.ListenAndServe()
	if err != nil {
		log.Fatal(err.Error())
	}
}
<commit_msg>CORS headers for current_action<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	log "github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/carbocation/interpose"
	gorilla_mux "github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/gorilla/mux"
	"github.com/liveplant/liveplant-server/Godeps/_workspace/src/github.com/tylerb/graceful"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"
	"time"
)

// Define string constants that correspond
// to each supported action here
const (
	ActionWater   string = "water"
	ActionNothing string = "nothing"
)

// Variables for keeping track of the current vote count
// for each action.
// These should probably be stored in redis at some point.
var VoteCountWater int = 0
var VoteCountNothing int = 0

type Application struct {
}

type CurrentAction struct {
	Action        string `json:"action"`
	UnixTimestamp int64  `json:"unixTimestamp"`
}

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

	json.NewEncoder(w).Encode(lastExecutedAction)
}

// Number of seconds before votes are processed and reset
const votingTimePeriod int64 = 30

var lastExecutedAction = CurrentAction{
	Action:        ActionNothing,
	UnixTimestamp: 0,
}

func update() {

	var currentTime = int64(time.Now().Unix())

	if (currentTime - lastExecutedAction.UnixTimestamp) >= votingTimePeriod {
		// The voting time period is over, so update the current winning vote

		lastExecutedAction.Action = GetWinningAction()
		lastExecutedAction.UnixTimestamp = currentTime

		resetVoteCount()
	}
}

func resetVoteCount() {
	VoteCountWater = 0
	VoteCountNothing = 0
}

func GetWinningAction() string {
	// Return the action that has the greatest number of votes

	var winningAction string

	if VoteCountWater > VoteCountNothing {
		winningAction = ActionWater
	} else {
		winningAction = ActionNothing
	}

	return winningAction
}

func DebugPrintHttpRequest(r *http.Request) {
	// If debug logger is enabled,
	// print out all the details of the supplied HTTP request.
	if log.GetLevel() == log.DebugLevel {
		dump, err := httputil.DumpRequest(r, true)
		if err == nil {
			log.Debug("Request received: \n" + string(dump))
		} else {
			log.Debug("Error reading request: " + err.Error())
		}
	}
}

/*
Example cURL commands for invoking the API on the commandline:

curl -H "Content-Type: application/json" -X POST -d '{"someKey1":"someValue1","someKey2":"someValue2"}' http://127.0.0.1:5000/votes
curl -H "Content-Type: application/json" -X POST -d '{"action":"water"}' http://127.0.0.1:5000/votes
curl -H "Content-Type: application/json" -X POST -d '{"action":"nothing"}' http://127.0.0.1:5000/votes

curl http://127.0.0.1:5000/current_action
*/

type Vote struct {
	Action string `json:"action"`
}

type VoteReceipt struct {
	Message string `json:"message"`
	Vote    Vote   `json:"vote"`
}

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

	log.Debug("PostVotes called")

	DebugPrintHttpRequest(r)

	decoder := json.NewDecoder(r.Body)
	var vote Vote
	var message string
	err := decoder.Decode(&vote)

	if err == nil {

		if vote.Action == ActionWater {
			VoteCountWater++
			message = fmt.Sprintf("Voted for action \"%s\" total count is %d", ActionWater, VoteCountWater)
		} else if vote.Action == ActionNothing {
			VoteCountNothing++
			message = fmt.Sprintf("Voted for action \"%s\" Total Count: %d", ActionNothing, VoteCountNothing)
		} else {
			message = fmt.Sprintf("Encountered unhandled action \"%s\"", vote.Action)
			// TODO: return a standard error object
			w.WriteHeader(http.StatusBadRequest)
		}
	} else {
		message = "Error parsing vote body: " + err.Error()
		w.WriteHeader(http.StatusBadRequest)
	}

	log.Debug(message)

	json.NewEncoder(w).Encode(&VoteReceipt{
		Message: message,
		Vote:    vote,
	})
}

type ActionInfo struct {
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
	VoteCount   int    `json:"voteCount"`
}

type CurrentVoteInfo struct {
	Actions []ActionInfo `json:"actions"`
}

/*
Example response from GET /votes
{
  "actions": [
    {
      "name": "nothing",
      "displayName": "Nothing",
      "voteCount": 123
    },
    {
      "name": "water",
      "displayName": "Water",
      "voteCount": 47
    }
  ]
}
*/
func GetVotes(w http.ResponseWriter, r *http.Request) {

	update()

	currentVoteInfo := &CurrentVoteInfo{
		Actions: []ActionInfo{
			ActionInfo{Name: ActionNothing, DisplayName: "Nothing", VoteCount: VoteCountNothing},
			ActionInfo{Name: ActionWater, DisplayName: "Water", VoteCount: VoteCountWater},
		},
	}

	json.NewEncoder(w).Encode(currentVoteInfo)
}
func NewApplication() (*Application, error) {
	app := &Application{}
	return app, nil
}

type preFlightHandler func(http.ResponseWriter, *http.Request)

func NewPreFlightHandler(methods ...string) preFlightHandler {
	return func(w http.ResponseWriter, r *http.Request) {
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ", "))
			w.Header().Set("Access-Control-Allow-Headers", "content-type")
		}
	}
}

func (app *Application) mux() *gorilla_mux.Router {
	router := gorilla_mux.NewRouter()

	router.HandleFunc("/current_action", GetCurrentAction).Methods("GET")
	router.HandleFunc("/current_action", NewPreFlightHandler("GET")).Methods("OPTIONS")
	router.HandleFunc("/votes", PostVotes).Methods("POST")
	router.HandleFunc("/votes", GetVotes).Methods("GET")
	router.HandleFunc("/votes", NewPreFlightHandler("GET", "POST")).Methods("OPTIONS")

	return router
}

func InitLogLevel() {
	// Check if --debug argument was supplied on the command line.
	// Check if LIVEPLANTDEBUG environment variable is present.
	// (Environment variable takes precedence over command line flag)
	// Enable or disable debug logger accordingly.

	// Declare and parse command line flag
	boolPtr := flag.Bool("debug", false, "Whether or not to enable debug logger.")
	flag.Parse()

	var debugLoggerEnabled bool = *boolPtr

	if len(os.Getenv("LIVEPLANTDEBUG")) > 0 {
		// Environment variable is present, so
		// debug logger should be enabled.
		// (overrides command line flag)
		debugLoggerEnabled = true
	}

	if debugLoggerEnabled {
		// Log everything
		log.SetLevel(log.DebugLevel)
		log.Debug("Debug logging enabled")
	} else {
		// Only log fatal or panic events
		// (events where the application is terminated)
		log.SetLevel(log.FatalLevel)
	}
}

func main() {

	InitLogLevel()

	log.Println("Launching liveplant server")

	app, _ := NewApplication()
	middle := interpose.New()

	middle.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			w.Header().Set("Access-Control-Allow-Origin", "*")
			next.ServeHTTP(w, req)
		})
	})

	middle.UseHandler(app.mux())

	ServerPort := "5000" // default port
	if len(os.Getenv("PORT")) > 0 {
		ServerPort = os.Getenv("PORT")
	}
	drainInterval, _ := time.ParseDuration("1s")
	srv := &graceful.Server{
		Timeout: drainInterval,
		Server: &http.Server{
			Addr:    ":" + ServerPort,
			Handler: middle,
		},
	}

	log.Println("Running liveplant server on port " + ServerPort)

	err := srv.ListenAndServe()
	if err != nil {
		log.Fatal(err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/signal"
	"time"

	"context"

	netContext "golang.org/x/net/context"

	"github.com/rusenask/keel/approvals"
	"github.com/rusenask/keel/bot"
	"github.com/rusenask/keel/cache/memory"

	"github.com/rusenask/keel/constants"
	"github.com/rusenask/keel/extension/notification"
	"github.com/rusenask/keel/provider"
	"github.com/rusenask/keel/provider/helm"
	"github.com/rusenask/keel/provider/kubernetes"
	"github.com/rusenask/keel/registry"
	"github.com/rusenask/keel/secrets"
	"github.com/rusenask/keel/trigger/http"
	"github.com/rusenask/keel/trigger/poll"
	"github.com/rusenask/keel/trigger/pubsub"
	"github.com/rusenask/keel/types"
	"github.com/rusenask/keel/util/codecs"
	"github.com/rusenask/keel/version"

	// extensions
	_ "github.com/rusenask/keel/extension/notification/slack"
	_ "github.com/rusenask/keel/extension/notification/webhook"

	log "github.com/Sirupsen/logrus"
)

// gcloud pubsub related config
const (
	EnvTriggerPubSub = "PUBSUB" // set to 1 or something to enable pub/sub trigger
	EnvTriggerPoll   = "POLL"   // set to 1 or something to enable poll trigger
	EnvProjectID     = "PROJECT_ID"

	EnvHelmProvider      = "HELM_PROVIDER"  // helm provider
	EnvHelmTillerAddress = "TILLER_ADDRESS" // helm provider
)

// kubernetes config, if empty - will default to InCluster
const (
	EnvKubernetesConfig = "KUBERNETES_CONFIG"
)

// EnvDebug - set to 1 or anything else to enable debug logging
const EnvDebug = "DEBUG"

func main() {

	ver := version.GetKeelVersion()
	log.WithFields(log.Fields{
		"os":         ver.OS,
		"build_date": ver.BuildDate,
		"revision":   ver.Revision,
		"version":    ver.Version,
		"go_version": ver.GoVersion,
		"arch":       ver.Arch,
	}).Info("keel starting..")

	if os.Getenv(EnvDebug) != "" {
		log.SetLevel(log.DebugLevel)
	}

	// setting up triggers
	ctx, cancel := netContext.WithCancel(context.Background())
	defer cancel()

	notifCfg := &notification.Config{
		Attempts: 10,
	}
	sender := notification.New(ctx)

	_, err := sender.Configure(notifCfg)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("main: failed to configure notification sender manager")
	}

	// getting k8s provider
	k8sCfg := &kubernetes.Opts{}
	if os.Getenv(EnvKubernetesConfig) != "" {
		k8sCfg.ConfigPath = os.Getenv(EnvKubernetesConfig)
	} else {
		k8sCfg.InCluster = true
	}
	implementer, err := kubernetes.NewKubernetesImplementer(k8sCfg)
	if err != nil {
		log.WithFields(log.Fields{
			"error":  err,
			"config": k8sCfg,
		}).Fatal("main: failed to create kubernetes implementer")
	}

	serializer := codecs.DefaultSerializer()
	mem := memory.NewMemoryCache(24*time.Hour, 24*time.Hour, 1*time.Minute)
	approvalsManager := approvals.New(mem, serializer)

	// setting up providers
	providers := setupProviders(implementer, sender, approvalsManager)

	secretsGetter := secrets.NewGetter(implementer)

	teardownTriggers := setupTriggers(ctx, providers, secretsGetter)

	teardownBot, err := setupBot(implementer, approvalsManager)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("main: failed to setup slack bot")
	}

	signalChan := make(chan os.Signal, 1)
	cleanupDone := make(chan bool)
	signal.Notify(signalChan, os.Interrupt)
	go func() {
		for _ = range signalChan {
			log.Info("received an interrupt, closing connection...")

			go func() {
				select {
				case <-time.After(10 * time.Second):
					log.Info("connection shutdown took too long, exiting... ")
					close(cleanupDone)
					return
				case <-cleanupDone:
					return
				}
			}()

			// teardownProviders()
			providers.Stop()
			teardownTriggers()
			teardownBot()

			cleanupDone <- true
		}
	}()

	<-cleanupDone
}

// setupProviders - setting up available providers. New providers should be initialised here and added to
// provider map
func setupProviders(k8sImplementer kubernetes.Implementer, sender notification.Sender, approvalsManager approvals.Manager) (providers provider.Providers) {
	var enabledProviders []provider.Provider

	k8sProvider, err := kubernetes.NewProvider(k8sImplementer, sender, approvalsManager)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("main.setupProviders: failed to create kubernetes provider")
	}
	go k8sProvider.Start()
	enabledProviders = append(enabledProviders, k8sProvider)

	if os.Getenv(EnvHelmProvider) == "1" {
		tillerAddr := os.Getenv(EnvHelmTillerAddress)
		helmImplementer := helm.NewHelmImplementer(tillerAddr)
		helmProvider := helm.NewProvider(helmImplementer, sender)

		go helmProvider.Start()
		enabledProviders = append(enabledProviders, helmProvider)
	}

	providers = provider.New(enabledProviders, approvalsManager)

	return providers
}

func setupBot(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manager) (teardown func(), err error) {

	if os.Getenv(constants.EnvSlackToken) != "" {
		botName := "keel"

		if os.Getenv(constants.EnvSlackBotName) != "" {
			botName = os.Getenv(constants.EnvSlackBotName)
		}

		token := os.Getenv(constants.EnvSlackToken)
		slackBot := bot.New(botName, token, k8sImplementer, approvalsManager)

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

		err := slackBot.Start(ctx)
		if err != nil {
			cancel()
			return nil, err
		}

		teardown := func() {
			// cancelling context
			cancel()
		}

		return teardown, nil
	}

	return func() {}, nil
}

// setupTriggers - setting up triggers. New triggers should be added to this function. Each trigger
// should go through all providers (or not if there is a reason) and submit events)
func setupTriggers(ctx context.Context, providers provider.Providers, secretsGetter secrets.Getter) (teardown func()) {

	// setting up generic http webhook server
	whs := http.NewTriggerServer(&http.Opts{
		Port:      types.KeelDefaultPort,
		Providers: providers,
	})

	go whs.Start()

	// checking whether pubsub (GCR) trigger is enabled
	if os.Getenv(EnvTriggerPubSub) != "" {
		projectID := os.Getenv(EnvProjectID)
		if projectID == "" {
			log.Fatalf("main.setupTriggers: project ID env variable not set")
			return
		}

		ps, err := pubsub.NewPubsubSubscriber(&pubsub.Opts{
			ProjectID: projectID,
			Providers: providers,
		})
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Fatal("main.setupTriggers: failed to create gcloud pubsub subscriber")
			return
		}

		subManager := pubsub.NewDefaultManager(projectID, providers, ps)
		go subManager.Start(ctx)
	}

	if os.Getenv(EnvTriggerPoll) != "0" {

		registryClient := registry.New()
		watcher := poll.NewRepositoryWatcher(providers, registryClient)
		pollManager := poll.NewPollManager(providers, watcher, secretsGetter)

		// start poll manager, will finish with ctx
		go watcher.Start(ctx)
		go pollManager.Start(ctx)
	}

	teardown = func() {
		whs.Stop()
	}

	return teardown
}
<commit_msg>preparing http trigger with approvals manager<commit_after>package main

import (
	"os"
	"os/signal"
	"time"

	"context"

	netContext "golang.org/x/net/context"

	"github.com/rusenask/keel/approvals"
	"github.com/rusenask/keel/bot"
	"github.com/rusenask/keel/cache/memory"

	"github.com/rusenask/keel/constants"
	"github.com/rusenask/keel/extension/notification"
	"github.com/rusenask/keel/provider"
	"github.com/rusenask/keel/provider/helm"
	"github.com/rusenask/keel/provider/kubernetes"
	"github.com/rusenask/keel/registry"
	"github.com/rusenask/keel/secrets"
	"github.com/rusenask/keel/trigger/http"
	"github.com/rusenask/keel/trigger/poll"
	"github.com/rusenask/keel/trigger/pubsub"
	"github.com/rusenask/keel/types"
	"github.com/rusenask/keel/util/codecs"
	"github.com/rusenask/keel/version"

	// extensions
	_ "github.com/rusenask/keel/extension/notification/slack"
	_ "github.com/rusenask/keel/extension/notification/webhook"

	log "github.com/Sirupsen/logrus"
)

// gcloud pubsub related config
const (
	EnvTriggerPubSub = "PUBSUB" // set to 1 or something to enable pub/sub trigger
	EnvTriggerPoll   = "POLL"   // set to 1 or something to enable poll trigger
	EnvProjectID     = "PROJECT_ID"

	EnvHelmProvider      = "HELM_PROVIDER"  // helm provider
	EnvHelmTillerAddress = "TILLER_ADDRESS" // helm provider
)

// kubernetes config, if empty - will default to InCluster
const (
	EnvKubernetesConfig = "KUBERNETES_CONFIG"
)

// EnvDebug - set to 1 or anything else to enable debug logging
const EnvDebug = "DEBUG"

func main() {

	ver := version.GetKeelVersion()
	log.WithFields(log.Fields{
		"os":         ver.OS,
		"build_date": ver.BuildDate,
		"revision":   ver.Revision,
		"version":    ver.Version,
		"go_version": ver.GoVersion,
		"arch":       ver.Arch,
	}).Info("keel starting..")

	if os.Getenv(EnvDebug) != "" {
		log.SetLevel(log.DebugLevel)
	}

	// setting up triggers
	ctx, cancel := netContext.WithCancel(context.Background())
	defer cancel()

	notifCfg := &notification.Config{
		Attempts: 10,
	}
	sender := notification.New(ctx)

	_, err := sender.Configure(notifCfg)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("main: failed to configure notification sender manager")
	}

	// getting k8s provider
	k8sCfg := &kubernetes.Opts{}
	if os.Getenv(EnvKubernetesConfig) != "" {
		k8sCfg.ConfigPath = os.Getenv(EnvKubernetesConfig)
	} else {
		k8sCfg.InCluster = true
	}
	implementer, err := kubernetes.NewKubernetesImplementer(k8sCfg)
	if err != nil {
		log.WithFields(log.Fields{
			"error":  err,
			"config": k8sCfg,
		}).Fatal("main: failed to create kubernetes implementer")
	}

	serializer := codecs.DefaultSerializer()
	mem := memory.NewMemoryCache(24*time.Hour, 24*time.Hour, 1*time.Minute)
	approvalsManager := approvals.New(mem, serializer)

	// setting up providers
	providers := setupProviders(implementer, sender, approvalsManager)

	secretsGetter := secrets.NewGetter(implementer)

	teardownTriggers := setupTriggers(ctx, providers, secretsGetter, approvalsManager)

	teardownBot, err := setupBot(implementer, approvalsManager)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("main: failed to setup slack bot")
	}

	signalChan := make(chan os.Signal, 1)
	cleanupDone := make(chan bool)
	signal.Notify(signalChan, os.Interrupt)
	go func() {
		for _ = range signalChan {
			log.Info("received an interrupt, closing connection...")

			go func() {
				select {
				case <-time.After(10 * time.Second):
					log.Info("connection shutdown took too long, exiting... ")
					close(cleanupDone)
					return
				case <-cleanupDone:
					return
				}
			}()

			// teardownProviders()
			providers.Stop()
			teardownTriggers()
			teardownBot()

			cleanupDone <- true
		}
	}()

	<-cleanupDone
}

// setupProviders - setting up available providers. New providers should be initialised here and added to
// provider map
func setupProviders(k8sImplementer kubernetes.Implementer, sender notification.Sender, approvalsManager approvals.Manager) (providers provider.Providers) {
	var enabledProviders []provider.Provider

	k8sProvider, err := kubernetes.NewProvider(k8sImplementer, sender, approvalsManager)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("main.setupProviders: failed to create kubernetes provider")
	}
	go k8sProvider.Start()
	enabledProviders = append(enabledProviders, k8sProvider)

	if os.Getenv(EnvHelmProvider) == "1" {
		tillerAddr := os.Getenv(EnvHelmTillerAddress)
		helmImplementer := helm.NewHelmImplementer(tillerAddr)
		helmProvider := helm.NewProvider(helmImplementer, sender)

		go helmProvider.Start()
		enabledProviders = append(enabledProviders, helmProvider)
	}

	providers = provider.New(enabledProviders, approvalsManager)

	return providers
}

func setupBot(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manager) (teardown func(), err error) {

	if os.Getenv(constants.EnvSlackToken) != "" {
		botName := "keel"

		if os.Getenv(constants.EnvSlackBotName) != "" {
			botName = os.Getenv(constants.EnvSlackBotName)
		}

		token := os.Getenv(constants.EnvSlackToken)
		slackBot := bot.New(botName, token, k8sImplementer, approvalsManager)

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

		err := slackBot.Start(ctx)
		if err != nil {
			cancel()
			return nil, err
		}

		teardown := func() {
			// cancelling context
			cancel()
		}

		return teardown, nil
	}

	return func() {}, nil
}

// setupTriggers - setting up triggers. New triggers should be added to this function. Each trigger
// should go through all providers (or not if there is a reason) and submit events)
func setupTriggers(ctx context.Context, providers provider.Providers, secretsGetter secrets.Getter, approvalsManager approvals.Manager) (teardown func()) {

	// setting up generic http webhook server
	whs := http.NewTriggerServer(&http.Opts{
		Port:            types.KeelDefaultPort,
		Providers:       providers,
		ApprovalManager: approvalsManager,
	})

	go whs.Start()

	// checking whether pubsub (GCR) trigger is enabled
	if os.Getenv(EnvTriggerPubSub) != "" {
		projectID := os.Getenv(EnvProjectID)
		if projectID == "" {
			log.Fatalf("main.setupTriggers: project ID env variable not set")
			return
		}

		ps, err := pubsub.NewPubsubSubscriber(&pubsub.Opts{
			ProjectID: projectID,
			Providers: providers,
		})
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Fatal("main.setupTriggers: failed to create gcloud pubsub subscriber")
			return
		}

		subManager := pubsub.NewDefaultManager(projectID, providers, ps)
		go subManager.Start(ctx)
	}

	if os.Getenv(EnvTriggerPoll) != "0" {

		registryClient := registry.New()
		watcher := poll.NewRepositoryWatcher(providers, registryClient)
		pollManager := poll.NewPollManager(providers, watcher, secretsGetter)

		// start poll manager, will finish with ctx
		go watcher.Start(ctx)
		go pollManager.Start(ctx)
	}

	teardown = func() {
		whs.Stop()
	}

	return teardown
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

var internRegex = regexp.MustCompile(`[^\w](?:interns?|internship)[^\w]`)

func panicIf(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	// Find the latest who's hiring post
	whoishiring, err := goquery.NewDocument("https://news.ycombinator.com/submitted?id=whoishiring")
	panicIf(err)

	var url string
	posts := whoishiring.Find("tr > td:nth-child(3)")
	for i := 0; i < posts.Length(); i++ {
		post := posts.Eq(i)
		title := post.Text()

		if strings.Contains(title, "Who is hiring?") {
			url, _ = post.Find("a").Attr("href")
			fmt.Println(title)
			break
		}
	}

	// Find and print all top level comments that match the intern regex
	latestPost, err := goquery.NewDocument("https://news.ycombinator.com/" + url)
	panicIf(err)

	totalComments := 0
	comments := latestPost.Find("table .comtr > td > table > tbody > tr")
	for i := 0; i < comments.Length(); i++ {
		comment := comments.Eq(i)

		width, _ := comment.Find(".ind img").Attr("width")
		if width == "0" {
			text := comment.Text()
			if internRegex.MatchString(text) {
				totalComments++
				fmt.Println("Intern Comment No.", totalComments)
				fmt.Println(text)
			}
		}
	}
}
<commit_msg>Color SF and DFW links, pipe through less to paginate.<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/fatih/color"
)

var (
	internRegex     = regexp.MustCompile(`(?i)[^\w](?:interns?|internship)[^\w]`)
	ansiEscapeRegex = regexp.MustCompile(`[[:cntrl:]]`)
	bayRegex        = regexp.MustCompile(`(?i)[^\w](?:san fran\w*|sf|bay area|mountain view|oakland|berkeley)[^\w]`)
	dallasRegex     = regexp.MustCompile(`(?i)[^\w](?:dallas|dfw|fort worth|richardson)[^\w]`)

	postTitleColor      = color.New(color.BgGreen).Add(color.Bold).SprintfFunc()
	commentTitleColor   = color.New(color.BgBlue).SprintfFunc()
	bayCommentColor     = color.New(color.FgMagenta).SprintfFunc()
	dallasCommentColor  = color.New(color.FgCyan).SprintfFunc()
	regularCommentColor = fmt.Sprintf
)

func panicIf(err error) {
	if err != nil {
		panic(err)
	}
}

func displayPaginatedString(paginatedString string, pager string) {
	cmd := exec.Command(pager)
	cmd.Stdin = strings.NewReader(paginatedString)
	cmd.Stdout = os.Stdout

	err := cmd.Run()
	panicIf(err)
}

func getLatestPost() (title string, url string) {
	whoishiring, err := goquery.NewDocument("https://news.ycombinator.com/submitted?id=whoishiring")
	panicIf(err)

	posts := whoishiring.Find("tr > td:nth-child(3)")
	for i := 0; i < posts.Length(); i++ {
		post := posts.Eq(i)
		title = post.Text()

		if strings.Contains(title, "Who is hiring?") {
			url, _ = post.Find("a").Attr("href")
			return title, url
		}
	}
	panic(errors.New("Error fetching/parsing whoishiring's posts."))
}

func getFormattedInternComments(url string) (formattedComments string) {
	latestPost, err := goquery.NewDocument("https://news.ycombinator.com/" + url)
	panicIf(err)

	totalComments := 0
	comments := latestPost.Find("table .comtr > td > table > tbody > tr")
	for i := 0; i < comments.Length(); i++ {
		comment := comments.Eq(i)

		width, _ := comment.Find(".ind img").Attr("width")
		if width == "0" {
			text := ansiEscapeRegex.ReplaceAllString(comment.Text(), "")
			if internRegex.MatchString(text) {
				var formatComment func(string, ...interface{}) string

				switch {
				case bayRegex.MatchString(text):
					formatComment = bayCommentColor
				case dallasRegex.MatchString(text):
					formatComment = dallasCommentColor
				default:
					formatComment = regularCommentColor
				}

				totalComments++
				formattedComments += "\n" // adding newlines to a template string with bgcolor makes it look bad
				formattedComments += commentTitleColor("Intern Comment No. %d", totalComments)
				formattedComments += "\n"
				formattedComments += formatComment("%s", text)
				formattedComments += "\n"
			}
		}
	}
	return
}

func main() {
	var paginatedString string
	pager := os.Getenv("PAGER")

	title, url := getLatestPost()
	paginatedString += postTitleColor("%s", title) + "\n"
	paginatedString += bayCommentColor("Bay Area Comments") + ", " + dallasCommentColor("Dallas Area Comments") + "\n"
	paginatedString += getFormattedInternComments(url)

	displayPaginatedString(paginatedString, pager)
}
<|endoftext|>