<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

const (
	numPollers     = 1
	pollInterval   = 60 * time.Second
	statusInterval = 1 * time.Minute
	errTimeout     = 10 * time.Second
)

var (
	threshold = flag.Uint64("threshold", uint64(1.074*10E11), "Threshold to start serving 500's over HTTP")
	addr      = flag.String("addr", ":8080", "Listen address for HTTP")
	path      = flag.String("path", "/mnt/storage", "The path to query for disk usage")
)

// State represents the last-known state of a path
type State struct {
	path  string
	bytes uint64
}

// StateMonitor maintains a map that stores the state of the URLs
// being
// polled, and prints the current state every updateInterval
// nanoseconds.
// It returns a chan State to which resource state should be sent.
func StateMonitor(updateInterval time.Duration) chan<- State {
	updates := make(chan State)
	diskStatus := make(map[string]uint64)
	ticker := time.NewTicker(updateInterval)
	go func() {
		for {
			select {
			case <-ticker.C:
				logState(diskStatus)
			case s := <-updates:
				diskStatus[s.path] = s.bytes
			}
		}
	}()
	go func() {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			bytes := diskStatus[*path]
			fmt.Fprintln(w, bytes)
		})
		if err := http.ListenAndServe(*addr, nil); err != nil {
			log.Fatal("ListenAndServe failed: ", err)
		}
	}()
	return updates
}

// logState prints a state map.
func logState(s map[string]uint64) {
	log.Println("Current state:")
	for k, v := range s {
		log.Printf(" %s %v", k, v)
	}
}

// Path represents a filesystem directory to be polled with du
type Path struct {
	path     string
	errCount int
}

// Poll executes du for a path
// and returns the disk usage in bytes or an error string
func (r *Path) Poll() (bytes uint64) {
	out, err := exec.Command("du", "-sbx", r.path).Output()
	if err != nil {
		log.Fatal(err)
		r.errCount++
	}
	// Tidy up the line
	s := string(out)
	s = strings.TrimSpace(s)

	// Parse tabulation
	bytes_str := strings.Split(s, "\t")[0]

	// Parse uint64 from string
	bytes, err = strconv.ParseUint(bytes_str, 0, 64)
	if err != nil {
		log.Fatal(err)
		r.errCount++
	}
	r.errCount = 0
	return bytes
}

// Sleep sleeps for an appropriate interval (dependent on error state)
// before sending the Path to done.
func (r *Path) Sleep(done chan<- *Path) {
	time.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))
	done <- r
}

func Poller(in <-chan *Path, out chan<- *Path, status chan<- State) {
	for r := range in {
		bytes := r.Poll()
		status <- State{r.path, bytes}
		out <- r
	}
}

func main() {
	// Create our input and output channels.
	pending, complete := make(chan *Path), make(chan *Path)

	// Launch the StateMonitor.
	status := StateMonitor(statusInterval)
	log.Println("State Monitor started")

	// Launch some Poller goroutines.
	for i := 0; i < numPollers; i++ {
		go Poller(pending, complete, status)
	}

	// Send the path flag to the pending queue
	go func() {
		pending <- &Path{path: *path}
	}()

	for r := range complete {
		go r.Sleep(pending)
	}

}
<commit_msg>add smarter error handling<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

const (
	numPollers     = 1
	pollInterval   = 60 * time.Second
	statusInterval = 1 * time.Minute
	errTimeout     = 10 * time.Second
)

var (
	threshold = flag.Uint64("threshold", uint64(1.074*10E11), "Threshold to start serving 500's over HTTP")
	addr      = flag.String("addr", ":8080", "Listen address for HTTP")
	path      = flag.String("path", "/mnt/storage", "The path to query for disk usage")
)

// State represents the last-known state of a path
type State struct {
	path  string
	bytes uint64
}

// StateMonitor maintains a map that stores the state of the URLs
// being
// polled, and prints the current state every updateInterval
// nanoseconds.
// It returns a chan State to which resource state should be sent.
func StateMonitor(updateInterval time.Duration) chan<- State {
	updates := make(chan State)
	diskStatus := make(map[string]uint64)
	ticker := time.NewTicker(updateInterval)
	go func() {
		for {
			select {
			case <-ticker.C:
				logState(diskStatus)
			case s := <-updates:
				diskStatus[s.path] = s.bytes
			}
		}
	}()
	go func() {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			bytes := diskStatus[*path]
			switch {
			case bytes == 0:
				http.Error(w, "Disk status not cached yet", http.StatusServiceUnavailable)
			case bytes > *threshold:
				err := fmt.Sprintf("ERROR: Bytes exceed threshold (%v/%v)", bytes, *threshold)
				http.Error(w, err, 500)
			default:
				fmt.Fprintf(w, "OK: %v\n", bytes)
			}
		})
		if err := http.ListenAndServe(*addr, nil); err != nil {
			log.Fatal("ListenAndServe failed: ", err)
		}
	}()
	return updates
}

// logState prints a state map.
func logState(s map[string]uint64) {
	log.Println("Current state:")
	for k, v := range s {
		log.Printf(" %s %v", k, v)
	}
}

// Path represents a filesystem directory to be polled with du
type Path struct {
	path     string
	errCount int
}

// Poll executes du for a path
// and returns the disk usage in bytes or an error string
func (r *Path) Poll() (bytes uint64) {
	out, err := exec.Command("du", "-sbx", r.path).Output()
	if err != nil {
		log.Fatal(err)
		r.errCount++
	}
	// Tidy up the line
	s := string(out)
	s = strings.TrimSpace(s)

	// Parse tabulation
	bytes_str := strings.Split(s, "\t")[0]

	// Parse uint64 from string
	bytes, err = strconv.ParseUint(bytes_str, 0, 64)
	if err != nil {
		log.Fatal(err)
		r.errCount++
	}
	r.errCount = 0
	return bytes
}

// Sleep sleeps for an appropriate interval (dependent on error state)
// before sending the Path to done.
func (r *Path) Sleep(done chan<- *Path) {
	time.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))
	done <- r
}

func Poller(in <-chan *Path, out chan<- *Path, status chan<- State) {
	for r := range in {
		bytes := r.Poll()
		status <- State{r.path, bytes}
		out <- r
	}
}

func main() {
	// parse CLI flags
	flag.Parse()

	// Create our input and output channels.
	pending, complete := make(chan *Path), make(chan *Path)

	// Launch the StateMonitor.
	status := StateMonitor(statusInterval)
	log.Println("State Monitor started")

	// Launch some Poller goroutines.
	for i := 0; i < numPollers; i++ {
		go Poller(pending, complete, status)
	}

	// Send the path flag to the pending queue
	go func() {
		pending <- &Path{path: *path}
	}()

	for r := range complete {
		go r.Sleep(pending)
	}

}
<|endoftext|>


<commit_before>package main // import "github.com/deshboard/boilerplate-service"

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/deshboard/boilerplate-service/app"
	"github.com/sagikazarmark/healthz"
)

func main() {
	defer handleShutdown()

	flag.Parse()

	logger.WithFields(logrus.Fields{
		"version":     app.Version,
		"commitHash":  app.CommitHash,
		"buildDate":   app.BuildDate,
		"environment": config.Environment,
	}).Printf("Starting %s service", app.FriendlyServiceName)

	w := logger.Logger.WriterLevel(logrus.ErrorLevel)
	shutdown = append(shutdown, w.Close)

	healthHandler, status := newHealthServiceHandler()
	healthServer := &http.Server{
		Addr:     config.HealthAddr,
		Handler:  healthHandler,
		ErrorLog: log.New(w, fmt.Sprintf("%s Health service: ", app.FriendlyServiceName), 0),
	}

	// Force closing server connections (if graceful closing fails)
	shutdown = append([]shutdownHandler{healthServer.Close}, shutdown...)

	errChan := make(chan error, 10)

	go func() {
		logger.WithField("addr", healthServer.Addr).Infof("%s Health service started", app.FriendlyServiceName)
		errChan <- healthServer.ListenAndServe()
	}()

	if config.Debug {
		debugServer := &http.Server{
			Addr:     config.DebugAddr,
			Handler:  http.DefaultServeMux,
			ErrorLog: log.New(w, fmt.Sprintf("%s Debug service: ", app.FriendlyServiceName), 0),
		}

		shutdown = append(shutdown, debugServer.Close)

		go func() {
			logger.WithField("addr", debugServer.Addr).Infof("%s Debug service started", app.FriendlyServiceName)
			errChan <- debugServer.ListenAndServe()
		}()
	}

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

MainLoop:
	for {
		select {
		case err := <-errChan:
			// In theory this can only be non-nil
			if err != nil {
				// This will be handled (logged) by shutdown
				panic(err)
			} else {
				logger.Info("Error channel received non-error value")

				// Break the loop, proceed with regular shutdown
				break MainLoop
			}
		case s := <-signalChan:
			logger.Println(fmt.Sprintf("Captured %v", s))
			status.SetStatus(healthz.Unhealthy)

			shutdownContext, shutdownCancel := context.WithTimeout(context.Background(), config.ShutdownTimeout)

			var wg sync.WaitGroup
			wg.Add(1)

			go func() {
				err := healthServer.Shutdown(shutdownContext)
				if err != nil {
					logger.Error(err)
				}

				wg.Done()
			}()

			wg.Wait()

			// Cancel context if shutdown completed earlier
			shutdownCancel()

			// Break the loop, proceed with regular shutdown
			break MainLoop
		}
	}

	close(errChan)
	close(signalChan)
}

type shutdownHandler func() error

// Wraps a function withot error return type
func shutdownFunc(fn func()) shutdownHandler {
	return func() error {
		fn()
		return nil
	}
}

// Panic recovery and shutdown handler
func handleShutdown() {
	v := recover()
	if v != nil {
		logger.Error(v)
	}

	logger.Info("Shutting down")

	for _, handler := range shutdown {
		err := handler()
		if err != nil {
			logger.Error(err)
		}
	}

	if v != nil {
		panic(v)
	}
}
<commit_msg>Simplify handling errors coming from the error channel<commit_after>package main // import "github.com/deshboard/boilerplate-service"

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/deshboard/boilerplate-service/app"
	"github.com/sagikazarmark/healthz"
)

func main() {
	defer handleShutdown()

	flag.Parse()

	logger.WithFields(logrus.Fields{
		"version":     app.Version,
		"commitHash":  app.CommitHash,
		"buildDate":   app.BuildDate,
		"environment": config.Environment,
	}).Printf("Starting %s service", app.FriendlyServiceName)

	w := logger.Logger.WriterLevel(logrus.ErrorLevel)
	shutdown = append(shutdown, w.Close)

	healthHandler, status := newHealthServiceHandler()
	healthServer := &http.Server{
		Addr:     config.HealthAddr,
		Handler:  healthHandler,
		ErrorLog: log.New(w, fmt.Sprintf("%s Health service: ", app.FriendlyServiceName), 0),
	}

	// Force close server connections (if graceful closing fails)
	shutdown = append([]shutdownHandler{healthServer.Close}, shutdown...)

	errChan := make(chan error, 10)

	go func() {
		logger.WithField("addr", healthServer.Addr).Infof("%s Health service started", app.FriendlyServiceName)
		errChan <- healthServer.ListenAndServe()
	}()

	if config.Debug {
		debugServer := &http.Server{
			Addr:     config.DebugAddr,
			Handler:  http.DefaultServeMux,
			ErrorLog: log.New(w, fmt.Sprintf("%s Debug service: ", app.FriendlyServiceName), 0),
		}

		shutdown = append(shutdown, debugServer.Close)

		go func() {
			logger.WithField("addr", debugServer.Addr).Infof("%s Debug service started", app.FriendlyServiceName)
			errChan <- debugServer.ListenAndServe()
		}()
	}

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

MainLoop:
	for {
		select {
		case err := <-errChan:
			if err != nil {
				logger.Error(err)
			} else {
				logger.Warning("Error channel received non-error value")
			}

			// Break the loop, proceed with regular shutdown
			break MainLoop
		case s := <-signalChan:
			logger.Println(fmt.Sprintf("Captured %v", s))
			status.SetStatus(healthz.Unhealthy)

			shutdownContext, shutdownCancel := context.WithTimeout(context.Background(), config.ShutdownTimeout)

			var wg sync.WaitGroup
			wg.Add(1)

			go func() {
				err := healthServer.Shutdown(shutdownContext)
				if err != nil {
					logger.Error(err)
				}

				wg.Done()
			}()

			wg.Wait()

			// Cancel context if shutdown completed earlier
			shutdownCancel()

			// Break the loop, proceed with regular shutdown
			break MainLoop
		}
	}

	close(errChan)
	close(signalChan)
}

type shutdownHandler func() error

// Wraps a function withot error return type
func shutdownFunc(fn func()) shutdownHandler {
	return func() error {
		fn()
		return nil
	}
}

// Panic recovery and shutdown handler
func handleShutdown() {
	v := recover()
	if v != nil {
		logger.Error(v)
	}

	logger.Info("Shutting down")

	for _, handler := range shutdown {
		err := handler()
		if err != nil {
			logger.Error(err)
		}
	}

	if v != nil {
		panic(v)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/nobonobo/p2pfw/peerconn"
	"github.com/nobonobo/p2pfw/signaling/client"
	"github.com/nobonobo/webrtc"
)

const usage = `Usage: ssh-p2p SUBCMD [options]
sub-commands:
	newkey
		new generate key of connection
	server -key="..." [-dial="127.0.0.1:22"]
		ssh server side peer mode
	client -key="..." [-listen="127.0.0.1:2222"]
		ssh client side peer mode
`

func main() {
	cmd := ""
	if len(os.Args) > 1 {
		cmd = os.Args[1]
	}
	var flags *flag.FlagSet
	flags = flag.NewFlagSet("", flag.ExitOnError)
	flags.Usage = func() {
		fmt.Fprintf(os.Stderr, usage)
		flags.PrintDefaults()
		os.Exit(1)
	}

	switch cmd {
	default:
		flags.Usage()
	case "newkey":
		key, err := client.UUID()
		if err != nil {
			log.Fatalln(err)
		}
		fmt.Println(key)
		os.Exit(0)
	case "server":
		var addr, key string
		flags.StringVar(&addr, "dial", "127.0.0.1:22", "dial addr = host:port")
		flags.StringVar(&key, "key", "sample", "connection key")
		if err := flags.Parse(os.Args[2:]); err != nil {
			log.Fatalln(err)
		}
		sig := make(chan os.Signal, 1)
		signal.Notify(sig, syscall.SIGINT)
		defer serve(key, addr)()
		<-sig
	case "client":
		var addr, key string
		flags.StringVar(&addr, "listen", "127.0.0.1:2222", "listen addr = host:port")
		flags.StringVar(&key, "key", "sample", "connection key")
		if err := flags.Parse(os.Args[2:]); err != nil {
			log.Fatalln(err)
		}
		l, err := net.Listen("tcp", addr)
		if err != nil {
			log.Fatalln(err)
		}
		log.Println("listen:", addr)
		for {
			sock, err := l.Accept()
			if err != nil {
				log.Println(err)
				continue
			}
			go connect(key, sock)
		}
	}
}

func serve(key, addr string) func() error {
	dial := new(client.Config)
	dial.RoomID = key
	dial.UserID = "***server***"
	dial.URL = "wss://signaling.arukascloud.io/ws"

	stun, err := peerconn.GetDefaultStunHosts()
	if err != nil {
		log.Fatalln(err)
	}
	config := webrtc.NewConfiguration()
	config.AddIceServer(stun)
	node, err := peerconn.NewNode(dial, config)
	if err != nil {
		log.Fatalln(err)
	}
	node.OnLeave = func(member string) {
		log.Println("leave:", member)
		node.Clients.Del(member)
	}
	node.OnPeerConnection = func(dest string, conn *peerconn.Conn) error {
		dc, err := conn.CreateDataChannel("default")
		if err != nil {
			return err
		}
		dc.OnOpen(func() {
			go func() {
				c := peerconn.NewDCConn(dc)
				defer c.Close()
				ssh, err := net.Dial("tcp", addr)
				if err != nil {
					log.Println("dial failed:", err)
					return
				}
				defer ssh.Close()
				log.Println("connected:", dest)
				go io.Copy(ssh, c)
				io.Copy(c, ssh)
			}()
		})
		dc.OnClose(func() {
			log.Println("disconnected:", dest)
			dc.Close()
		})
		return nil
	}
	if err := node.Start(true); err != nil {
		log.Fatalln(err)
	}
	return node.Close
}

func connect(key string, sock net.Conn) {
	id, err := client.UUID()
	if err != nil {
		log.Fatalln(err)
	}

	dial := new(client.Config)
	dial.RoomID = key
	dial.UserID = id
	dial.URL = "wss://signaling.arukascloud.io/ws"

	stun, err := peerconn.GetDefaultStunHosts()
	if err != nil {
		log.Fatalln(err)
	}
	config := webrtc.NewConfiguration()
	config.AddIceServer(stun)
	node, err := peerconn.NewNode(dial, config)
	if err != nil {
		log.Fatalln(err)
	}
	if err := node.Start(false); err != nil {
		log.Fatalln(err)
	}
	members, err := node.Members()
	if err != nil {
		log.Fatalln(err)
	}
	conn, err := node.Connect(members.Owner)
	if err != nil {
		log.Fatalln(err)
	}
	conn.OnDataChannel(func(dc *webrtc.DataChannel) {
		go func() {
			defer func() {
				if err := node.Close(); err != nil {
					log.Println(err)
				}
			}()
			log.Println("data channel open:", dc)
			defer log.Println("data channel close:", dc)
			c := peerconn.NewDCConn(dc)
			go func() {
				defer func() {
					if err := c.Close(); err != nil {
						log.Println(err)
					}
				}()
				if _, err := io.Copy(c, sock); err != nil {
					log.Println(err)
				}
			}()
			defer func() {
				if err := sock.Close(); err != nil {
					log.Println(err)
				}
			}()
			if _, err := io.Copy(sock, c); err != nil {
				log.Println(err)
			}
		}()
	})
}
<commit_msg>url fix<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/nobonobo/p2pfw/peerconn"
	"github.com/nobonobo/p2pfw/signaling/client"
	"github.com/nobonobo/webrtc"
)

const usage = `Usage: ssh-p2p SUBCMD [options]
sub-commands:
	newkey
		new generate key of connection
	server -key="..." [-dial="127.0.0.1:22"]
		ssh server side peer mode
	client -key="..." [-listen="127.0.0.1:2222"]
		ssh client side peer mode
`

func main() {
	cmd := ""
	if len(os.Args) > 1 {
		cmd = os.Args[1]
	}
	var flags *flag.FlagSet
	flags = flag.NewFlagSet("", flag.ExitOnError)
	flags.Usage = func() {
		fmt.Fprintf(os.Stderr, usage)
		flags.PrintDefaults()
		os.Exit(1)
	}

	switch cmd {
	default:
		flags.Usage()
	case "newkey":
		key, err := client.UUID()
		if err != nil {
			log.Fatalln(err)
		}
		fmt.Println(key)
		os.Exit(0)
	case "server":
		var addr, key string
		flags.StringVar(&addr, "dial", "127.0.0.1:22", "dial addr = host:port")
		flags.StringVar(&key, "key", "sample", "connection key")
		if err := flags.Parse(os.Args[2:]); err != nil {
			log.Fatalln(err)
		}
		sig := make(chan os.Signal, 1)
		signal.Notify(sig, syscall.SIGINT)
		defer serve(key, addr)()
		<-sig
	case "client":
		var addr, key string
		flags.StringVar(&addr, "listen", "127.0.0.1:2222", "listen addr = host:port")
		flags.StringVar(&key, "key", "sample", "connection key")
		if err := flags.Parse(os.Args[2:]); err != nil {
			log.Fatalln(err)
		}
		l, err := net.Listen("tcp", addr)
		if err != nil {
			log.Fatalln(err)
		}
		log.Println("listen:", addr)
		for {
			sock, err := l.Accept()
			if err != nil {
				log.Println(err)
				continue
			}
			go connect(key, sock)
		}
	}
}

func serve(key, addr string) func() error {
	dial := new(client.Config)
	dial.RoomID = key
	dial.UserID = "***server***"
	dial.URL = "wss://signaling.irieda.com/ws"

	stun, err := peerconn.GetDefaultStunHosts()
	if err != nil {
		log.Fatalln(err)
	}
	config := webrtc.NewConfiguration()
	config.AddIceServer(stun)
	node, err := peerconn.NewNode(dial, config)
	if err != nil {
		log.Fatalln(err)
	}
	node.OnLeave = func(member string) {
		log.Println("leave:", member)
		node.Clients.Del(member)
	}
	node.OnPeerConnection = func(dest string, conn *peerconn.Conn) error {
		dc, err := conn.CreateDataChannel("default")
		if err != nil {
			return err
		}
		dc.OnOpen(func() {
			go func() {
				c := peerconn.NewDCConn(dc)
				defer c.Close()
				ssh, err := net.Dial("tcp", addr)
				if err != nil {
					log.Println("dial failed:", err)
					return
				}
				defer ssh.Close()
				log.Println("connected:", dest)
				go io.Copy(ssh, c)
				io.Copy(c, ssh)
			}()
		})
		dc.OnClose(func() {
			log.Println("disconnected:", dest)
			dc.Close()
		})
		return nil
	}
	if err := node.Start(true); err != nil {
		log.Fatalln(err)
	}
	return node.Close
}

func connect(key string, sock net.Conn) {
	id, err := client.UUID()
	if err != nil {
		log.Fatalln(err)
	}

	dial := new(client.Config)
	dial.RoomID = key
	dial.UserID = id
	dial.URL = "wss://signaling.irieda.com/ws"

	stun, err := peerconn.GetDefaultStunHosts()
	if err != nil {
		log.Fatalln(err)
	}
	config := webrtc.NewConfiguration()
	config.AddIceServer(stun)
	node, err := peerconn.NewNode(dial, config)
	if err != nil {
		log.Fatalln(err)
	}
	if err := node.Start(false); err != nil {
		log.Fatalln(err)
	}
	members, err := node.Members()
	if err != nil {
		log.Fatalln(err)
	}
	conn, err := node.Connect(members.Owner)
	if err != nil {
		log.Fatalln(err)
	}
	conn.OnDataChannel(func(dc *webrtc.DataChannel) {
		go func() {
			defer func() {
				if err := node.Close(); err != nil {
					log.Println(err)
				}
			}()
			log.Println("data channel open:", dc)
			defer log.Println("data channel close:", dc)
			c := peerconn.NewDCConn(dc)
			go func() {
				defer func() {
					if err := c.Close(); err != nil {
						log.Println(err)
					}
				}()
				if _, err := io.Copy(c, sock); err != nil {
					log.Println(err)
				}
			}()
			defer func() {
				if err := sock.Close(); err != nil {
					log.Println(err)
				}
			}()
			if _, err := io.Copy(sock, c); err != nil {
				log.Println(err)
			}
		}()
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"path"

	"time"

	"strings"

	"github.com/SlyMarbo/rss"
)

func watchRSS(ctx *Context, rssurl string, category string, delay time.Duration) {
	log.Printf("RSS [%s] : %s\n", category, rssurl)
	feed, err := rss.Fetch(rssurl)
	if err != nil {
		panic(err)
	}

	for i := 0; ; {
		for _, item := range feed.Items {
			version := strings.Replace(item.Title, "Patch", "", -1)
			version = strings.Trim(version, " ")
			row := VersionRow{
				version:  version,
				category: category,
				date:     item.Date,
				link:     item.Link,
			}

			if i == 0 {
				ctx.modeCh <- insertModeForce
				ctx.rowCh <- row
			} else {
				ctx.modeCh <- insertModeCheckVersion
				ctx.rowCh <- row
			}
		}

		time.Sleep(delay)
		feed.Update()
		log.Printf("RSS[%s] : %s\n", category, rssurl)
	}
}

func watchLatestVersion(ctx *Context, category string, delay time.Duration) {
	for i := 0; ; {
		f := &RealHTTPFetcher{}
		version := getLatestVersion(f)
		log.Printf("Latest Version [%s] : %s\n", category, version)

		link := makeStableReleaseNoteURL(version)
		row := VersionRow{
			version:  version,
			category: category,
			date:     time.Now(),
			link:     link,
		}

		if i == 0 {
			ctx.modeCh <- insertModeForce
			ctx.rowCh <- row
		} else {
			ctx.modeCh <- insertModeCheckVersion
			ctx.rowCh <- row
		}

		time.Sleep(delay)
	}
}

var logfilename string

func init() {
	flag.StringVar(&logfilename, "log", "", "log filename")
}

type Context struct {
	config   *Config
	accessor *DatabaseAccessor

	modeCh chan int
	rowCh  chan VersionRow
}

var ctx Context

func main() {
	flag.Parse()

	// initialize logger
	// http: //stackoverflow.com/questions/19965795/go-golang-write-log-to-file
	// logger 초기화를 별도 함수에서 할 경우 defer 로 파일이 닫혀서 로그작성이 안된다
	// 그래서 그냥 메인함수에서 처리
	if logfilename != "" {
		filepath := path.Join(getExecutablePath(), logfilename)
		f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalf("error opening file: %v", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	db := NewDB("db.sqlite3")
	defer db.close()

	c := NewConfig()
	c = nil

	ctx = Context{
		config:   c,
		accessor: NewDBAccessor(db, NewSender(c)),
		modeCh:   make(chan int, 10),
		rowCh:    make(chan VersionRow, 10),
	}

	interval := 15 * time.Minute
	go watchRSS(&ctx, rssPatch, categoryPatch, interval)
	go watchRSS(&ctx, rssBeta, categoryBeta, interval)
	go watchLatestVersion(&ctx, categoryStable, interval)

	ctx.accessor.Run(ctx.modeCh, ctx.rowCh)
}
<commit_msg>카운터 증가시키는걸 잊어버렸다<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"path"

	"time"

	"strings"

	"github.com/SlyMarbo/rss"
)

func watchRSS(ctx *Context, rssurl string, category string, delay time.Duration) {
	log.Printf("RSS [%s] : %s\n", category, rssurl)
	feed, err := rss.Fetch(rssurl)
	if err != nil {
		panic(err)
	}

	for i := 0; ; i++ {
		for _, item := range feed.Items {
			version := strings.Replace(item.Title, "Patch", "", -1)
			version = strings.Trim(version, " ")
			row := VersionRow{
				version:  version,
				category: category,
				date:     item.Date,
				link:     item.Link,
			}

			if i == 0 {
				ctx.modeCh <- insertModeForce
				ctx.rowCh <- row
			} else {
				ctx.modeCh <- insertModeCheckVersion
				ctx.rowCh <- row
			}
		}

		time.Sleep(delay)
		feed.Update()
		log.Printf("RSS[%s] : %s\n", category, rssurl)
	}
}

func watchLatestVersion(ctx *Context, category string, delay time.Duration) {
	for i := 0; ; i++ {
		f := &RealHTTPFetcher{}
		version := getLatestVersion(f)
		log.Printf("Latest Version [%s] : %s\n", category, version)

		link := makeStableReleaseNoteURL(version)
		row := VersionRow{
			version:  version,
			category: category,
			date:     time.Now(),
			link:     link,
		}

		if i == 0 {
			ctx.modeCh <- insertModeForce
			ctx.rowCh <- row
		} else {
			ctx.modeCh <- insertModeCheckVersion
			ctx.rowCh <- row
		}

		time.Sleep(delay)
	}
}

var logfilename string

func init() {
	flag.StringVar(&logfilename, "log", "", "log filename")
}

type Context struct {
	config   *Config
	accessor *DatabaseAccessor

	modeCh chan int
	rowCh  chan VersionRow
}

var ctx Context

func main() {
	flag.Parse()

	// initialize logger
	// http: //stackoverflow.com/questions/19965795/go-golang-write-log-to-file
	// logger 초기화를 별도 함수에서 할 경우 defer 로 파일이 닫혀서 로그작성이 안된다
	// 그래서 그냥 메인함수에서 처리
	if logfilename != "" {
		filepath := path.Join(getExecutablePath(), logfilename)
		f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalf("error opening file: %v", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	db := NewDB("db.sqlite3")
	defer db.close()

	c := NewConfig()
	c = nil

	ctx = Context{
		config:   c,
		accessor: NewDBAccessor(db, NewSender(c)),
		modeCh:   make(chan int, 10),
		rowCh:    make(chan VersionRow, 10),
	}

	interval := 15 * time.Minute
	go watchRSS(&ctx, rssPatch, categoryPatch, interval)
	go watchRSS(&ctx, rssBeta, categoryBeta, interval)
	go watchLatestVersion(&ctx, categoryStable, interval)

	ctx.accessor.Run(ctx.modeCh, ctx.rowCh)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/juju/errors"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	compute "google.golang.org/api/compute/v1"
)

var (
	flagDisk = flag.String("disk", "", "disk name to attach to the instance")
	flagPath = flag.String("path", "", "path in the current instance to mount the disk")
)

func main() {
	if err := runSafe(); err != nil {
		log.Println(errors.ErrorStack(err))
	}
}

func runSafe() error {
	flag.Parse()

	if *flagDisk == "" || *flagPath == "" {
		return errors.NotValidf("--disk and --path are required")
	}

	log.Println(" [*] Attaching disk", *flagDisk, "to the instance in path: ", *flagPath)

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.ComputeTokenSource(""),
		},
	}
	service, err := compute.New(client)
	if err != nil {
		return errors.Trace(err)
	}

	log.Println(" > Get metadata...")
	project, err := getMetadata("project/project-id")
	if err != nil {
		return errors.Trace(err)
	}

	rawZone, err := getMetadata("instance/zone")
	if err != nil {
		return errors.Trace(err)
	}
	parts := strings.Split(rawZone, "/")
	zone := parts[len(parts)-1]

	rawInstanceName, err := getMetadata("instance/hostname")
	if err != nil {
		return errors.Trace(err)
	}
	parts = strings.Split(rawInstanceName, ".")
	instanceName := parts[0]

	log.Println(" > Check disk name is correct...")
	if err := checkDiskExists(service, project, zone); err != nil {
		return errors.Trace(err)
	}

	log.Println(" > Check if there is another instance with the disk...")
	oldInstance, err := findAttachedInstance(service, project, zone)
	if err != nil {
		return errors.Trace(err)
	}
	if oldInstance != instanceName {
		if oldInstance != "" {
			log.Println(" > Deattaching disk from instance:", oldInstance)
			if err := detachDisk(service, project, zone, oldInstance); err != nil {
				return errors.Trace(err)
			}
		}

		log.Println(" > Attach disk to this instance...")
		if err := attachDisk(service, project, zone, instanceName); err != nil {
			return errors.Trace(err)
		}
	}

	log.Println(" [*] Disk attached successfully!")

	return nil
}

func getMetadata(path string) (string, error) {
	u := fmt.Sprintf("http://metadata.google.internal/computeMetadata/v1/%s", path)
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return "", errors.Trace(err)
	}
	req.Header.Set("Metadata-Flavor", "Google")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", errors.Trace(err)
	}
	defer resp.Body.Close()

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", errors.Trace(err)
	}

	return string(content), nil
}

func checkDiskExists(service *compute.Service, project, zone string) error {
	_, err := service.Disks.Get(project, zone, *flagDisk).Do()
	if err != nil {
		return errors.Trace(err)
	}

	return nil
}

func findAttachedInstance(service *compute.Service, project, zone string) (string, error) {
	instances, err := service.Instances.List(project, zone).Do()
	if err != nil {
		return "", errors.Trace(err)
	}
	for _, instance := range instances.Items {
		for _, disk := range instance.Disks {
			if disk.DeviceName == *flagDisk {
				return instance.Name, nil
			}
		}
	}

	return "", nil
}

func detachDisk(service *compute.Service, project, zone, instance string) error {
	resp, err := service.Instances.DetachDisk(project, zone, instance, *flagDisk).Do()
	if err != nil {
		return errors.Trace(err)
	}

	for true {
		operationResult, err := service.ZoneOperations.Get(project, zone, resp.Name).Do()
		if err != nil {
			return errors.Trace(err)
		}

		if operationResult.Error != nil {
			return errors.Errorf("operation error, see: %s", resp.Name)
		}

		if operationResult.Status == "DONE" {
			break
		}
	}

	return nil
}

func attachDisk(service *compute.Service, project, zone, instance string) error {
	disk := &compute.AttachedDisk{
		DeviceName: *flagDisk,
		Source:     fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/zones/%s/disks/%s", project, zone, *flagDisk),
	}
	resp, err := service.Instances.AttachDisk(project, zone, instance, disk).Do()
	if err != nil {
		return errors.Trace(err)
	}

	for true {
		operationResult, err := service.ZoneOperations.Get(project, zone, resp.Name).Do()
		if err != nil {
			return errors.Trace(err)
		}

		if operationResult.Error != nil {
			return errors.Errorf("operation error, see: %s", resp.Name)
		}

		if operationResult.Status == "DONE" {
			break
		}
	}

	return nil
}
<commit_msg>Sleep between operation checks. Don't receive a path flag.<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/juju/errors"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	compute "google.golang.org/api/compute/v1"
)

var (
	flagDisk = flag.String("disk", "", "disk name to attach to the instance")
)

func main() {
	if err := runSafe(); err != nil {
		log.Println(errors.ErrorStack(err))
	}
}

func runSafe() error {
	flag.Parse()

	if *flagDisk == "" {
		return errors.NotValidf("--disk flag is required")
	}

	log.Println(" [*] Attaching disk", *flagDisk, "to the instance...")

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.ComputeTokenSource(""),
		},
	}
	service, err := compute.New(client)
	if err != nil {
		return errors.Trace(err)
	}

	log.Println(" > Get metadata...")
	project, err := getMetadata("project/project-id")
	if err != nil {
		return errors.Trace(err)
	}

	rawZone, err := getMetadata("instance/zone")
	if err != nil {
		return errors.Trace(err)
	}
	parts := strings.Split(rawZone, "/")
	zone := parts[len(parts)-1]

	rawInstanceName, err := getMetadata("instance/hostname")
	if err != nil {
		return errors.Trace(err)
	}
	parts = strings.Split(rawInstanceName, ".")
	instanceName := parts[0]

	log.Println(" > Check disk name is correct...")
	if err := checkDiskExists(service, project, zone); err != nil {
		return errors.Trace(err)
	}

	log.Println(" > Check if there is another instance with the disk...")
	oldInstance, err := findAttachedInstance(service, project, zone)
	if err != nil {
		return errors.Trace(err)
	}
	if oldInstance != instanceName {
		if oldInstance != "" {
			log.Println(" > Deattaching disk from instance:", oldInstance)
			if err := detachDisk(service, project, zone, oldInstance); err != nil {
				return errors.Trace(err)
			}
		}

		log.Println(" > Attach disk to this instance...")
		if err := attachDisk(service, project, zone, instanceName); err != nil {
			return errors.Trace(err)
		}
	}

	log.Println(" [*] Disk attached successfully!")

	return nil
}

func getMetadata(path string) (string, error) {
	u := fmt.Sprintf("http://metadata.google.internal/computeMetadata/v1/%s", path)
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return "", errors.Trace(err)
	}
	req.Header.Set("Metadata-Flavor", "Google")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", errors.Trace(err)
	}
	defer resp.Body.Close()

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", errors.Trace(err)
	}

	return string(content), nil
}

func checkDiskExists(service *compute.Service, project, zone string) error {
	_, err := service.Disks.Get(project, zone, *flagDisk).Do()
	if err != nil {
		return errors.Trace(err)
	}

	return nil
}

func findAttachedInstance(service *compute.Service, project, zone string) (string, error) {
	instances, err := service.Instances.List(project, zone).Do()
	if err != nil {
		return "", errors.Trace(err)
	}
	for _, instance := range instances.Items {
		for _, disk := range instance.Disks {
			if disk.DeviceName == *flagDisk {
				return instance.Name, nil
			}
		}
	}

	return "", nil
}

func detachDisk(service *compute.Service, project, zone, instance string) error {
	resp, err := service.Instances.DetachDisk(project, zone, instance, *flagDisk).Do()
	if err != nil {
		return errors.Trace(err)
	}

	for true {
		operationResult, err := service.ZoneOperations.Get(project, zone, resp.Name).Do()
		if err != nil {
			return errors.Trace(err)
		}

		if operationResult.Error != nil {
			return errors.Errorf("operation error, see: %s", resp.Name)
		}

		if operationResult.Status == "DONE" {
			break
		}

		time.Sleep(5 * time.Second)
	}

	return nil
}

func attachDisk(service *compute.Service, project, zone, instance string) error {
	disk := &compute.AttachedDisk{
		DeviceName: *flagDisk,
		Source:     fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/zones/%s/disks/%s", project, zone, *flagDisk),
	}
	resp, err := service.Instances.AttachDisk(project, zone, instance, disk).Do()
	if err != nil {
		return errors.Trace(err)
	}

	for true {
		operationResult, err := service.ZoneOperations.Get(project, zone, resp.Name).Do()
		if err != nil {
			return errors.Trace(err)
		}

		if operationResult.Error != nil {
			return errors.Errorf("operation error, see: %s", resp.Name)
		}

		if operationResult.Status == "DONE" {
			break
		}

		time.Sleep(5 * time.Second)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/drone/drone-plugin-go/plugin"
	"golang.org/x/crypto/ssh"
)

// Params stores the git clone parameters used to
// configure and customzie the git clone behavior.
type Params struct {
	Commands []string `json:"commands"`
	Login    string   `json:"user"`
	Port     int      `json:"port"`
	Host     StrSlice `json:"host"`
	Sleep    int      `json:"sleep"`
}

func main() {
	v := new(Params)
	w := new(plugin.Workspace)
	plugin.Param("workspace", w)
	plugin.Param("vargs", &v)
	plugin.MustParse()

	for i, host := range v.Host.Slice() {
		err := run(w.Keys, v, host)
		if err != nil {
			os.Exit(1)
		}
		if v.Sleep != 0 && i > v.Host.Len()-1 {
			fmt.Printf("$ sleep %d\n", v.Sleep)
			time.Sleep(time.Duration(v.Sleep) * time.Second)
		}
	}
}

func run(keys *plugin.Keypair, params *Params, host string) error {

	// if no username is provided assume root
	if len(params.Login) == 0 {
		params.Login = "root"
	}

	// if no username is provided assume root
	if params.Port == 0 {
		params.Port = 22
	}

	// join the host and port if necessary
	addr := net.JoinHostPort(
		host,
		strconv.Itoa(params.Port),
	)

	// trace command used for debugging in the build logs
	fmt.Printf("$ ssh %s@%s -p %d\n", params.Login, addr, params.Port)

	signer, err := ssh.ParsePrivateKey([]byte(keys.Private))
	if err != nil {
		return fmt.Errorf("Error parsing private key. %s.", err)
	}

	config := &ssh.ClientConfig{
		User: params.Login,
		Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
	}

	client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return fmt.Errorf("Error dialing server. %s.", err)
	}

	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("Error starting ssh session. %s.", err)
	}
	defer session.Close()

	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	return session.Run(strings.Join(params.Commands, "\n"))
}
<commit_msg>fixed botched if statement<commit_after>package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/drone/drone-plugin-go/plugin"
	"golang.org/x/crypto/ssh"
)

// Params stores the git clone parameters used to
// configure and customzie the git clone behavior.
type Params struct {
	Commands []string `json:"commands"`
	Login    string   `json:"user"`
	Port     int      `json:"port"`
	Host     StrSlice `json:"host"`
	Sleep    int      `json:"sleep"`
}

func main() {
	v := new(Params)
	w := new(plugin.Workspace)
	plugin.Param("workspace", w)
	plugin.Param("vargs", &v)
	plugin.MustParse()

	for i, host := range v.Host.Slice() {
		err := run(w.Keys, v, host)
		if err != nil {
			os.Exit(1)
		}
		if v.Sleep != 0 && i != v.Host.Len()-1 {
			fmt.Printf("$ sleep %d\n", v.Sleep)
			time.Sleep(time.Duration(v.Sleep) * time.Second)
		}
	}
}

func run(keys *plugin.Keypair, params *Params, host string) error {

	// if no username is provided assume root
	if len(params.Login) == 0 {
		params.Login = "root"
	}

	// if no username is provided assume root
	if params.Port == 0 {
		params.Port = 22
	}

	// join the host and port if necessary
	addr := net.JoinHostPort(
		host,
		strconv.Itoa(params.Port),
	)

	// trace command used for debugging in the build logs
	fmt.Printf("$ ssh %s@%s -p %d\n", params.Login, addr, params.Port)

	signer, err := ssh.ParsePrivateKey([]byte(keys.Private))
	if err != nil {
		return fmt.Errorf("Error parsing private key. %s.", err)
	}

	config := &ssh.ClientConfig{
		User: params.Login,
		Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
	}

	client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return fmt.Errorf("Error dialing server. %s.", err)
	}

	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("Error starting ssh session. %s.", err)
	}
	defer session.Close()

	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	return session.Run(strings.Join(params.Commands, "\n"))
}
<|endoftext|>


<commit_before>package discordgo

import (
	"encoding/json"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type wsWriter struct {
	session *Session

	conn           net.Conn
	closer         chan interface{}
	incoming       chan interface{}
	sendCloseQueue chan []byte

	writer *wsutil.Writer
}

func (w *wsWriter) Run() {
	w.writer = wsutil.NewWriter(w.conn, ws.StateClientSide, ws.OpText)

	for {
		select {
		case <-w.closer:
			select {
			// Ensure we send the close frame
			case body := <-w.sendCloseQueue:
				w.sendClose(body)
			default:
			}
			return
		case msg := <-w.incoming:
			var err error
			switch t := msg.(type) {
			case []byte:
				err = w.writeRaw(t)
			default:
				err = w.writeJson(t)
			}

			if err != nil {
				w.session.log(LogError, "Error writing to gateway: %s", err.Error())
				return
			}
		case body := <-w.sendCloseQueue:
			w.sendClose(body)
		}
	}
}

func (w *wsWriter) writeJson(data interface{}) error {
	serialized, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return w.writeRaw(serialized)
}

func (w *wsWriter) writeRaw(data []byte) error {
	_, err := w.writer.WriteThrough(data)
	if err != nil {
		return err
	}

	return w.writer.Flush()
}

func (w *wsWriter) sendClose(body []byte) error {
	_, err := w.conn.Write(body)
	return err
}

func (w *wsWriter) Queue(data interface{}) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.incoming <- data:
	}
}

func (w *wsWriter) QueueClose(body []byte) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.sendCloseQueue <- body:
	}
}

type wsHeartBeater struct {
	sync.Mutex

	writer      *wsWriter
	sequence    *int64
	receivedAck bool
	missedAcks  int
	stop        chan interface{}

	// Called when we received no Ack from last heartbeat
	onNoAck func()

	lastAck  time.Time
	lastSend time.Time
}

func (wh *wsHeartBeater) ReceivedAck() {
	wh.Lock()
	wh.receivedAck = true
	wh.missedAcks = 0
	wh.lastAck = time.Now()
	wh.Unlock()
}

func (wh *wsHeartBeater) UpdateSequence(seq int64) {
	atomic.StoreInt64(wh.sequence, seq)
}

func (wh *wsHeartBeater) Run(interval time.Duration) {
	ticker := time.NewTicker(interval)

	for {
		select {
		case <-ticker.C:

			wh.Lock()
			hasReceivedAck := wh.receivedAck
			wh.receivedAck = false
			wh.missedAcks++
			missed := wh.missedAcks

			wh.lastSend = time.Now()
			wh.Unlock()

			if !hasReceivedAck && wh.onNoAck != nil && missed > 4 {
				wh.onNoAck()
			}

			wh.SendBeat()
		case <-wh.stop:
			return
		}
	}
}

func (wh *wsHeartBeater) SendBeat() {
	seq := atomic.LoadInt64(wh.sequence)

	wh.writer.Queue(&outgoingEvent{
		Operation: GatewayOPHeartbeat,
		Data:      seq,
	})
}

func (wh *wsHeartBeater) Times() (send time.Time, ack time.Time) {
	wh.Lock()
	send = wh.lastSend
	ack = wh.lastAck
	wh.Unlock()
	return
}
<commit_msg>ratelimit sending on gateway<commit_after>package discordgo

import (
	"encoding/json"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type wsWriter struct {
	session *Session

	conn           net.Conn
	closer         chan interface{}
	incoming       chan interface{}
	sendCloseQueue chan []byte

	writer *wsutil.Writer

	sendRatelimiter chan bool
}

func (w *wsWriter) Run() {
	w.sendRatelimiter = make(chan bool)
	go w.runSendRatelimiter()

	w.writer = wsutil.NewWriter(w.conn, ws.StateClientSide, ws.OpText)

	for {
		select {
		case <-w.closer:
			select {
			// Ensure we send the close frame
			case body := <-w.sendCloseQueue:
				w.sendClose(body)
			default:
			}
			return
		case msg := <-w.incoming:
			var err error
			switch t := msg.(type) {
			case []byte:
				err = w.writeRaw(t)
			default:
				err = w.writeJson(t)
			}

			if err != nil {
				w.session.log(LogError, "Error writing to gateway: %s", err.Error())
				return
			}
		case body := <-w.sendCloseQueue:
			w.sendClose(body)
		}
	}
}

func (w *wsWriter) runSendRatelimiter() {
	tc := time.NewTicker(time.Millisecond * 500)
	defer tc.Stop()
	defer func() {
		close(w.sendRatelimiter)
	}()

	for {
		select {
		case <-tc.C:
			select {
			case w.sendRatelimiter <- true:
			case <-w.closer:
				return
			}
		case <-w.closer:
			return
		}
	}
}

func (w *wsWriter) writeJson(data interface{}) error {
	serialized, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return w.writeRaw(serialized)
}

func (w *wsWriter) writeRaw(data []byte) error {
	<-w.sendRatelimiter

	_, err := w.writer.WriteThrough(data)
	if err != nil {
		return err
	}

	return w.writer.Flush()
}

func (w *wsWriter) sendClose(body []byte) error {
	_, err := w.conn.Write(body)
	return err
}

func (w *wsWriter) Queue(data interface{}) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.incoming <- data:
	}
}

func (w *wsWriter) QueueClose(body []byte) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.sendCloseQueue <- body:
	}
}

type wsHeartBeater struct {
	sync.Mutex

	writer      *wsWriter
	sequence    *int64
	receivedAck bool
	missedAcks  int
	stop        chan interface{}

	// Called when we received no Ack from last heartbeat
	onNoAck func()

	lastAck  time.Time
	lastSend time.Time
}

func (wh *wsHeartBeater) ReceivedAck() {
	wh.Lock()
	wh.receivedAck = true
	wh.missedAcks = 0
	wh.lastAck = time.Now()
	wh.Unlock()
}

func (wh *wsHeartBeater) UpdateSequence(seq int64) {
	atomic.StoreInt64(wh.sequence, seq)
}

func (wh *wsHeartBeater) Run(interval time.Duration) {
	ticker := time.NewTicker(interval)

	for {
		select {
		case <-ticker.C:

			wh.Lock()
			hasReceivedAck := wh.receivedAck
			wh.receivedAck = false
			wh.missedAcks++
			missed := wh.missedAcks

			wh.lastSend = time.Now()
			wh.Unlock()

			if !hasReceivedAck && wh.onNoAck != nil && missed > 4 {
				wh.onNoAck()
			}

			wh.SendBeat()
		case <-wh.stop:
			return
		}
	}
}

func (wh *wsHeartBeater) SendBeat() {
	seq := atomic.LoadInt64(wh.sequence)

	wh.writer.Queue(&outgoingEvent{
		Operation: GatewayOPHeartbeat,
		Data:      seq,
	})
}

func (wh *wsHeartBeater) Times() (send time.Time, ack time.Time) {
	wh.Lock()
	send = wh.lastSend
	ack = wh.lastAck
	wh.Unlock()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"

	"github.com/WeCanHearYou/wechy/app/identity"
	"github.com/WeCanHearYou/wechy/app/infra"
	"github.com/WeCanHearYou/wechy/app/postgres"
	"github.com/WeCanHearYou/wechy/app/toolbox/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/driver/postgres"
	mig "github.com/mattes/migrate/migrate"

	"fmt"
)

var buildtime string
var version = "0.0.0"

func migrate() {
	fmt.Printf("Running migrations... \n")
	errors, ok := mig.UpSync(env.MustGet("DATABASE_URL"), env.Path("/migrations"))
	if !ok {
		for i, err := range errors {
			fmt.Printf("Error #%d: %s.\n", i, err)
		}

		panic("Migrations failed.")
	} else {
		fmt.Printf("Migrations finished with success.\n")
	}
}

func init() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())
	migrate()
}

func main() {
	db, err := sql.Open("postgres", env.MustGet("DATABASE_URL"))
	if err != nil {
		panic(err)
	}

	ctx := &WechyServices{
		OAuth:  &identity.HTTPOAuthService{},
		Health: &postgres.HealthCheckService{DB: db},
		Idea:   &postgres.IdeaService{DB: db},
		User:   &postgres.UserService{DB: db},
		Tenant: &postgres.TenantService{DB: db},
		Settings: &infra.WechySettings{
			BuildTime:    buildtime,
			Version:      version,
			AuthEndpoint: env.MustGet("AUTH_ENDPOINT"),
		},
	}

	e := GetMainEngine(ctx)
	e.Logger.Fatal(e.Start(":" + env.GetEnvOrDefault("PORT", "3000")))
}
<commit_msg>v0.1.0<commit_after>package main

import (
	"database/sql"

	"github.com/WeCanHearYou/wechy/app/identity"
	"github.com/WeCanHearYou/wechy/app/infra"
	"github.com/WeCanHearYou/wechy/app/postgres"
	"github.com/WeCanHearYou/wechy/app/toolbox/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/driver/postgres"
	mig "github.com/mattes/migrate/migrate"

	"fmt"
)

var buildtime string
var version = "0.1.0"

func migrate() {
	fmt.Printf("Running migrations... \n")
	errors, ok := mig.UpSync(env.MustGet("DATABASE_URL"), env.Path("/migrations"))
	if !ok {
		for i, err := range errors {
			fmt.Printf("Error #%d: %s.\n", i, err)
		}

		panic("Migrations failed.")
	} else {
		fmt.Printf("Migrations finished with success.\n")
	}
}

func init() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())
	migrate()
}

func main() {
	db, err := sql.Open("postgres", env.MustGet("DATABASE_URL"))
	if err != nil {
		panic(err)
	}

	ctx := &WechyServices{
		OAuth:  &identity.HTTPOAuthService{},
		Health: &postgres.HealthCheckService{DB: db},
		Idea:   &postgres.IdeaService{DB: db},
		User:   &postgres.UserService{DB: db},
		Tenant: &postgres.TenantService{DB: db},
		Settings: &infra.WechySettings{
			BuildTime:    buildtime,
			Version:      version,
			AuthEndpoint: env.MustGet("AUTH_ENDPOINT"),
		},
	}

	e := GetMainEngine(ctx)
	e.Logger.Fatal(e.Start(":" + env.GetEnvOrDefault("PORT", "3000")))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"

	"github.com/cloudflare/cfssl/cli"
	"github.com/cloudflare/cfssl/cli/genkey"
	"github.com/cloudflare/cfssl/cli/sign"
	"github.com/cloudflare/cfssl/config"
	"github.com/cloudflare/cfssl/csr"
	"github.com/cloudflare/cfssl/signer"
)

type cfg struct {
	interval time.Duration
	perms    uint

	csrfile string
	cfgfile string

	certpath string
	certname string
}

var c = &cfg{}
var s = &cli.Config{}

func init() {
	flag.DurationVar(&c.interval, "interval", 0, "repeat interval")
	flag.UintVar(&c.perms, "perms", 0600, "permissions for resulting dirs/files")

	flag.StringVar(&c.csrfile, "csrfile", "", "path to csr json file")
	flag.StringVar(&c.cfgfile, "config", "", "path to file with auth info")

	flag.StringVar(&c.certpath, "certpath", "", "path of resulting cert and key")
	flag.StringVar(&c.certname, "certname", "", "name of resulting cert and key (${certname}.pem, ${certname}-key.pem)")

	flag.StringVar(&s.Hostname, "hostname", "", "hostname for the cert, comma separated")
	flag.StringVar(&s.Profile, "profile", "", "signing config profile")
	flag.StringVar(&s.Label, "label", "", "signing config label")
	flag.StringVar(&s.Remote, "remote", "", "remote cfssl server")
}

func main() {
	flag.Parse()

	if c.interval == 0 {
		log.Fatal("interval is required and must be > 0")
	}

	if len(c.csrfile) == 0 {
		log.Fatal("csrfile is required")
	}

	if len(c.certpath) == 0 {
		log.Fatal("path is required")
	}

	if len(c.certname) == 0 {
		log.Fatal("certname is required")
	}

	if len(s.Remote) == 0 {
		log.Fatal("remote is required")
	}

	log.Println("cfssl-sidecar")
	log.Printf("operating with interval %s", c.interval)

	if err := os.MkdirAll(c.certpath, os.FileMode(c.perms)); err != nil {
		log.Fatal(err)
	}

	if len(c.cfgfile) > 0 {
		loadedCfg, err := config.LoadFile(c.cfgfile)
		if err != nil {
			log.Fatal(err)
		}
		s.CFG = loadedCfg
	} else {
		log.Println("WARNING: no config file specified.")
	}

	log.Println("updating key and csr.")
	keypath, csrpath, err := createKey(c)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("key(%s) and csr(%s) updated.", *keypath, *csrpath)

	s.CSRFile = *csrpath
	for {
		log.Println("updating certificate.")
		certpath, err := createCert(c, s)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("cert(%s) updated.", *certpath)

		log.Printf("sleeping for interval %s.", c.interval)
		time.Sleep(c.interval)
	}
}

func createKey(c *cfg) (*string, *string, error) {
	keypath := path.Join(c.certpath, fmt.Sprintf("%s-key.pem", c.certname))
	csrpath := path.Join(c.certpath, fmt.Sprintf("%s.csr", c.certname))

	_, keyerr := os.Stat(keypath)
	_, csrerr := os.Stat(csrpath)

	if keyerr != nil && csrerr != nil {
		log.Println("key and csr already exist")
		return &keypath, &csrpath, nil
	}

	bytes, err := ioutil.ReadFile(c.csrfile)
	if err != nil {
		return nil, nil, err
	}

	req := csr.CertificateRequest{KeyRequest: csr.NewBasicKeyRequest()}
	if err := json.Unmarshal(bytes, &req); err != nil {
		return nil, nil, err
	}

	var keybytes, csrbytes []byte
	g := &csr.Generator{Validator: genkey.Validator}
	csrbytes, keybytes, err = g.ProcessRequest(&req)
	if err != nil {
		return nil, nil, err
	}

	if err := ioutil.WriteFile(keypath, keybytes, 0600); err != nil {
		return nil, nil, err
	}

	if err := ioutil.WriteFile(csrpath, csrbytes, 0600); err != nil {
		return nil, nil, err
	}

	return &keypath, &csrpath, nil
}

func createCert(c *cfg, s *cli.Config) (*string, error) {
	signr, err := sign.SignerFromConfig(*s)
	if err != nil {
		return nil, err
	}

	csrbytes, err := ioutil.ReadFile(s.CSRFile)
	if err != nil {
		return nil, err
	}

	var cert []byte
	signReq := signer.SignRequest{
		Request: string(csrbytes),
		Hosts:   signer.SplitHosts(s.Hostname),
		Profile: s.Profile,
		Label:   s.Label,
	}

	cert, err = signr.Sign(signReq)
	if err != nil {
		return nil, err
	}

	certpath := path.Join(c.certpath, fmt.Sprintf("%s.pem", c.certname))
	if err := ioutil.WriteFile(certpath, cert, 0600); err != nil {
		return nil, err
	}

	return &certpath, nil
}
<commit_msg>negate condition<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"

	"github.com/cloudflare/cfssl/cli"
	"github.com/cloudflare/cfssl/cli/genkey"
	"github.com/cloudflare/cfssl/cli/sign"
	"github.com/cloudflare/cfssl/config"
	"github.com/cloudflare/cfssl/csr"
	"github.com/cloudflare/cfssl/signer"
)

type cfg struct {
	interval time.Duration
	perms    uint

	csrfile string
	cfgfile string

	certpath string
	certname string
}

var c = &cfg{}
var s = &cli.Config{}

func init() {
	flag.DurationVar(&c.interval, "interval", 0, "repeat interval")
	flag.UintVar(&c.perms, "perms", 0600, "permissions for resulting dirs/files")

	flag.StringVar(&c.csrfile, "csrfile", "", "path to csr json file")
	flag.StringVar(&c.cfgfile, "config", "", "path to file with auth info")

	flag.StringVar(&c.certpath, "certpath", "", "path of resulting cert and key")
	flag.StringVar(&c.certname, "certname", "", "name of resulting cert and key (${certname}.pem, ${certname}-key.pem)")

	flag.StringVar(&s.Hostname, "hostname", "", "hostname for the cert, comma separated")
	flag.StringVar(&s.Profile, "profile", "", "signing config profile")
	flag.StringVar(&s.Label, "label", "", "signing config label")
	flag.StringVar(&s.Remote, "remote", "", "remote cfssl server")
}

func main() {
	flag.Parse()

	if c.interval == 0 {
		log.Fatal("interval is required and must be > 0")
	}

	if len(c.csrfile) == 0 {
		log.Fatal("csrfile is required")
	}

	if len(c.certpath) == 0 {
		log.Fatal("path is required")
	}

	if len(c.certname) == 0 {
		log.Fatal("certname is required")
	}

	if len(s.Remote) == 0 {
		log.Fatal("remote is required")
	}

	log.Println("cfssl-sidecar")
	log.Printf("operating with interval %s", c.interval)

	if err := os.MkdirAll(c.certpath, os.FileMode(c.perms)); err != nil {
		log.Fatal(err)
	}

	if len(c.cfgfile) > 0 {
		loadedCfg, err := config.LoadFile(c.cfgfile)
		if err != nil {
			log.Fatal(err)
		}
		s.CFG = loadedCfg
	} else {
		log.Println("WARNING: no config file specified.")
	}

	log.Println("updating key and csr.")
	keypath, csrpath, err := createKey(c)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("key(%s) and csr(%s) up to date.", *keypath, *csrpath)

	s.CSRFile = *csrpath
	for {
		log.Println("updating certificate.")
		certpath, err := createCert(c, s)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("cert(%s) updated.", *certpath)

		log.Printf("sleeping for interval %s.", c.interval)
		time.Sleep(c.interval)
	}
}

func createKey(c *cfg) (*string, *string, error) {
	keypath := path.Join(c.certpath, fmt.Sprintf("%s-key.pem", c.certname))
	csrpath := path.Join(c.certpath, fmt.Sprintf("%s.csr", c.certname))

	_, keyerr := os.Stat(keypath)
	_, csrerr := os.Stat(csrpath)

	if keyerr == nil && csrerr == nil {
		log.Println("key and csr already exist")
		return &keypath, &csrpath, nil
	}

	bytes, err := ioutil.ReadFile(c.csrfile)
	if err != nil {
		return nil, nil, err
	}

	req := csr.CertificateRequest{KeyRequest: csr.NewBasicKeyRequest()}
	if err := json.Unmarshal(bytes, &req); err != nil {
		return nil, nil, err
	}

	var keybytes, csrbytes []byte
	g := &csr.Generator{Validator: genkey.Validator}
	csrbytes, keybytes, err = g.ProcessRequest(&req)
	if err != nil {
		return nil, nil, err
	}

	if err := ioutil.WriteFile(keypath, keybytes, 0600); err != nil {
		return nil, nil, err
	}

	if err := ioutil.WriteFile(csrpath, csrbytes, 0600); err != nil {
		return nil, nil, err
	}

	return &keypath, &csrpath, nil
}

func createCert(c *cfg, s *cli.Config) (*string, error) {
	signr, err := sign.SignerFromConfig(*s)
	if err != nil {
		return nil, err
	}

	csrbytes, err := ioutil.ReadFile(s.CSRFile)
	if err != nil {
		return nil, err
	}

	var cert []byte
	signReq := signer.SignRequest{
		Request: string(csrbytes),
		Hosts:   signer.SplitHosts(s.Hostname),
		Profile: s.Profile,
		Label:   s.Label,
	}

	cert, err = signr.Sign(signReq)
	if err != nil {
		return nil, err
	}

	certpath := path.Join(c.certpath, fmt.Sprintf("%s.pem", c.certname))
	if err := ioutil.WriteFile(certpath, cert, 0600); err != nil {
		return nil, err
	}

	return &certpath, nil
}
<|endoftext|>


<commit_before>// Enhanced Markdown template processor.
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/mh-cbon/emd/cli"
	"github.com/mh-cbon/emd/deprecated"
	"github.com/mh-cbon/emd/emd"
	gostd "github.com/mh-cbon/emd/go"
	gononstd "github.com/mh-cbon/emd/go-nonstd"
	"github.com/mh-cbon/emd/std"
)

// VERSION defines the running build id.
var VERSION = "0.0.0"

var program = cli.NewProgram("emd", VERSION)

func main() {
	program.Bind()
	if err := program.Run(os.Args); err != nil {
		panic(err)
	}
}

// gen sub command
type gencommand struct {
	*cli.Command
	in        string
	out       string
	data      string
	help      bool
	shortHelp bool
}

func init() {
	gen := &gencommand{Command: cli.NewCommand("gen", "Process an emd file.", Generate)}
	gen.Set.StringVar(&gen.in, "in", "", "Input src file")
	gen.Set.StringVar(&gen.out, "out", "-", "Output destination, defaults to stdout")
	gen.Set.StringVar(&gen.data, "data", "", "JSON map of data")
	gen.Set.BoolVar(&gen.help, "help", false, "Show help")
	gen.Set.BoolVar(&gen.shortHelp, "h", false, "Show help")

	program.Add(gen)
}

// Generate is the cli command implementation of gen.
func Generate(s cli.Commander) error {

	cmd, ok := s.(*gencommand)
	if ok == false {
		return fmt.Errorf("Invalid command type %T", s)
	}

	if cmd.help || cmd.shortHelp {
		return program.ShowCmdUsage(cmd)
	}

	out, err := getStdout(cmd.out)
	if err != nil {
		return err
	}
	if x, ok := out.(io.Closer); ok {
		defer x.Close()
	}

	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Failed to determmine cwd: %v", err)
	}
	// for those who uses symlinks to relocate their code,
	// the path must be evaluated.
	cwd, err = filepath.EvalSymlinks(cwd)
	if err != nil {
		return fmt.Errorf("Failed to determmine eval path: %v", err)
	}

	plugins := map[string]func(*emd.Generator) error{
		"std":        std.Register,
		"gostd":      gostd.Register,
		"gononstd":   gononstd.Register,
		"deprecated": deprecated.Register,
	}

	data := map[string]interface{}{
		"Name":         filepath.Base(cwd),
		"User":         getProjectUser(cwd),
		"ProviderURL":  getProviderURL(cwd),
		"ProviderName": getProviderName(cwd),
		"URL":          getProviderURL(cwd) + "/" + getProjectUser(cwd) + "/" + filepath.Base(cwd),
		"Branch":       "master",
	}

	if cmd.data != "" {
		if err := json.Unmarshal([]byte(cmd.data), &data); err != nil {
			return fmt.Errorf("Cannot decode JSON data string: %v", err)
		}
	}

	gen := emd.NewGenerator()

	if cmd.in != "" {
		if err := gen.AddFileTemplate(cmd.in); err != nil {
			return err
		}
	} else {
		gen.AddTemplate(defTemplate)
	}

	gen.SetDataMap(data)

	for name, plugin := range plugins {
		if err := plugin(gen); err != nil {
			return fmt.Errorf("Failed to register %v package: %v", name, err)
		}
	}

	if err := gen.Execute(out); err != nil {
		return fmt.Errorf("Generator failed: %v", err)
	}

	if cmd.out != "-" {
		fmt.Println("")
		fmt.Println("Success")
	}

	return nil
}

func getStdout(out string) (io.Writer, error) {

	ret := os.Stdout

	if out != "-" {
		f, err := os.Create(out)
		if err != nil {
			f, err = os.Open(out)
			if err != nil {
				return nil, fmt.Errorf("Cannot open out destination: %v", err)
			}
		}
		ret = f
	}
	return ret, nil
}

func getProjectUser(s string) string {
	ss := strings.Split(s, "/")
	if len(ss) > 2 {
		return ss[len(ss)-2]
	}
	return ""
}

func getProviderName(s string) string {
	if strings.Index(s, "github.com/") > -1 {
		return "github"
	}
	if strings.Index(s, "gitlab.com/") > -1 {
		return "gitlab"
	}
	// etc
	return ""
}

func getProviderURL(s string) string {
	if strings.Index(s, "github.com/") > -1 {
		return "github.com"
	}
	if strings.Index(s, "gitlab.com/") > -1 {
		return "gitlab.com"
	}
	// etc
	return ""
}

var defTemplate = `# {{.Name}}

{{template "badge/goreport" .}} {{template "badge/godoc" .}}

{{pkgdoc}}

# {{toc 5}}

# Install

{{template "gh/releases" .}}

#### go
{{template "go/install" .}}
`
<commit_msg>fix default reading of the md file<commit_after>// Enhanced Markdown template processor.
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/mh-cbon/emd/cli"
	"github.com/mh-cbon/emd/deprecated"
	"github.com/mh-cbon/emd/emd"
	gostd "github.com/mh-cbon/emd/go"
	gononstd "github.com/mh-cbon/emd/go-nonstd"
	"github.com/mh-cbon/emd/std"
)

// VERSION defines the running build id.
var VERSION = "0.0.0"

var program = cli.NewProgram("emd", VERSION)

func main() {
	program.Bind()
	if err := program.Run(os.Args); err != nil {
		panic(err)
	}
}

// gen sub command
type gencommand struct {
	*cli.Command
	in        string
	out       string
	data      string
	help      bool
	shortHelp bool
}

func init() {
	gen := &gencommand{Command: cli.NewCommand("gen", "Process an emd file.", Generate)}
	gen.Set.StringVar(&gen.in, "in", "", "Input src file")
	gen.Set.StringVar(&gen.out, "out", "-", "Output destination, defaults to stdout")
	gen.Set.StringVar(&gen.data, "data", "", "JSON map of data")
	gen.Set.BoolVar(&gen.help, "help", false, "Show help")
	gen.Set.BoolVar(&gen.shortHelp, "h", false, "Show help")

	program.Add(gen)
}

// Generate is the cli command implementation of gen.
func Generate(s cli.Commander) error {

	cmd, ok := s.(*gencommand)
	if ok == false {
		return fmt.Errorf("Invalid command type %T", s)
	}

	if cmd.help || cmd.shortHelp {
		return program.ShowCmdUsage(cmd)
	}

	out, err := getStdout(cmd.out)
	if err != nil {
		return err
	}
	if x, ok := out.(io.Closer); ok {
		defer x.Close()
	}

	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Failed to determmine cwd: %v", err)
	}
	// for those who uses symlinks to relocate their code,
	// the path must be evaluated.
	cwd, err = filepath.EvalSymlinks(cwd)
	if err != nil {
		return fmt.Errorf("Failed to determmine eval path: %v", err)
	}

	plugins := map[string]func(*emd.Generator) error{
		"std":        std.Register,
		"gostd":      gostd.Register,
		"gononstd":   gononstd.Register,
		"deprecated": deprecated.Register,
	}

	data := map[string]interface{}{
		"Name":         filepath.Base(cwd),
		"User":         getProjectUser(cwd),
		"ProviderURL":  getProviderURL(cwd),
		"ProviderName": getProviderName(cwd),
		"URL":          getProviderURL(cwd) + "/" + getProjectUser(cwd) + "/" + filepath.Base(cwd),
		"Branch":       "master",
	}

	if cmd.data != "" {
		if err := json.Unmarshal([]byte(cmd.data), &data); err != nil {
			return fmt.Errorf("Cannot decode JSON data string: %v", err)
		}
	}

	gen := emd.NewGenerator()

	if cmd.in != "" {
		if err := gen.AddFileTemplate(cmd.in); err != nil {
			return err
		}
	} else if s, err := os.Stat("README.e.md"); !os.IsNotExist(err) && s.IsDir() == false {
		if err := gen.AddFileTemplate("README.e.md"); err != nil {
			return err
		}
	} else {
		gen.AddTemplate(defTemplate)
	}

	gen.SetDataMap(data)

	for name, plugin := range plugins {
		if err := plugin(gen); err != nil {
			return fmt.Errorf("Failed to register %v package: %v", name, err)
		}
	}

	if err := gen.Execute(out); err != nil {
		return fmt.Errorf("Generator failed: %v", err)
	}

	if cmd.out != "-" {
		fmt.Println("")
		fmt.Println("Success")
	}

	return nil
}

func getStdout(out string) (io.Writer, error) {

	ret := os.Stdout

	if out != "-" {
		f, err := os.Create(out)
		if err != nil {
			f, err = os.Open(out)
			if err != nil {
				return nil, fmt.Errorf("Cannot open out destination: %v", err)
			}
		}
		ret = f
	}
	return ret, nil
}

func getProjectUser(s string) string {
	ss := strings.Split(s, "/")
	if len(ss) > 2 {
		return ss[len(ss)-2]
	}
	return ""
}

func getProviderName(s string) string {
	if strings.Index(s, "github.com/") > -1 {
		return "github"
	}
	if strings.Index(s, "gitlab.com/") > -1 {
		return "gitlab"
	}
	// etc
	return ""
}

func getProviderURL(s string) string {
	if strings.Index(s, "github.com/") > -1 {
		return "github.com"
	}
	if strings.Index(s, "gitlab.com/") > -1 {
		return "gitlab.com"
	}
	// etc
	return ""
}

var defTemplate = `# {{.Name}}

{{template "badge/goreport" .}} {{template "badge/godoc" .}}

{{pkgdoc}}

# {{toc 5}}

# Install

{{template "gh/releases" .}}

#### go
{{template "go/install" .}}
`
<|endoftext|>


<commit_before>package feeds

import (
	"net/http"

	"github.com/gorilla/mux"
	"github.com/rs/cors"
	"google.golang.org/appengine"
)

func Run() {
	dao := datastoreFeedsDao{}
	router := mux.NewRouter()

	router.Handle("/feeds", cors.Default().Handler(getFeedsHandler{dao})).
		Methods(http.MethodGet)

	getHandler := cors.Default().Handler(getFeedHandler{dao})
	router.Handle("/feeds/{feedId}", getHandler).
		Methods(http.MethodGet)

	postHandler := cors.Default().Handler(postHttpHandler{appengine.NewContext, fetchRssWithUrlFetch, dao})
	router.Handle("/feeds", postHandler).
		Methods(http.MethodPost).
		Headers("Content-Type", "application/json")

	http.Handle("/", cors.Default().Handler(router))
}<commit_msg>Add CORS handler for Location header<commit_after>package feeds

import (
	"net/http"

	"github.com/gorilla/mux"
	"github.com/rs/cors"
	"google.golang.org/appengine"
)

func Run() {
	dao := datastoreFeedsDao{}
	router := mux.NewRouter()

	corsHandler := cors.New(cors.Options{
		AllowedHeaders:{"Location"},
	})
	router.Handle("/feeds", corsHandler(getFeedsHandler{dao})).
		Methods(http.MethodGet)

	getHandler := corsHandler(getFeedHandler{dao})
	router.Handle("/feeds/{feedId}", getHandler).
		Methods(http.MethodGet)

	postHandler := corsHandler(postHttpHandler{appengine.NewContext, fetchRssWithUrlFetch, dao})
	router.Handle("/feeds", postHandler).
		Methods(http.MethodPost).
		Headers("Content-Type", "application/json")

	http.Handle("/", corsHandler(router))
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/ogier/pflag"
	"github.com/yuya-takeyama/argf"
)

var (
	name    = "ltsvp"
	version = "0.4.2"

	flagset         = pflag.NewFlagSet(name, pflag.ContinueOnError)
	list            = flagset.StringP("keys", "k", "", "")
	outputDelimiter = flagset.StringP("output-delimiter", "D", "\t", "")
	remainLTSV      = flagset.BoolP("remain-ltsv", "r", false, "")
	isHelp          = flagset.BoolP("help", "h", false, "")
	isVersion       = flagset.BoolP("version", "v", false, "")
)

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s OPTION... [FILE]...
Print selected parts of LTSV from each FILE to standard output.

Options:
  -k, --keys=LIST
                 select only these keys (required)
  -D, --output-delimiter=STRING
                 use STRING to separate parts (default: \t)
  -r, --remain-ltsv
                 print selected parts as LTSV
  -h, --help
                 display this help text and exit
  -v, --version
                 output version information and exit

LIST is made up of keys separated by commas.
  host           # Select host
  host,time,ua   # Select host, time, and ua
`[1:], name)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func printErr(err interface{}) {
	fmt.Fprintf(os.Stderr, "%s: %s\n", name, err)
}

func guideToHelp() {
	fmt.Fprintf(os.Stderr, "Try '%s --help' for more information.\n", name)
}

func do(l *LTSVScanner) error {
	for l.Scan() {
		fmt.Println(l.Text())
	}
	return l.Err()
}

func main() {
	flagset.SetOutput(ioutil.Discard)
	if err := flagset.Parse(os.Args[1:]); err != nil {
		printErr(err)
		guideToHelp()
		os.Exit(2)
	}
	switch {
	case *isHelp:
		printUsage()
		os.Exit(0)
	case *isVersion:
		printVersion()
		os.Exit(0)
	}

	if *list == "" {
		printErr("no specify LIST")
		guideToHelp()
		os.Exit(2)
	}
	keys := ParseKeysList(*list)

	r, err := argf.From(flagset.Args())
	if err != nil {
		printErr(err)
		guideToHelp()
		os.Exit(2)
	}

	l := NewLTSVScanner(keys, r)
	l.OutputDelimiter = *outputDelimiter
	l.RemainLTSV = *remainLTSV
	if err := do(l); err != nil {
		printErr(err)
		os.Exit(1)
	}
	os.Exit(0)
}
<commit_msg>Use pflag.FlagSet.Visit to check whether specified LIST<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/ogier/pflag"
	"github.com/yuya-takeyama/argf"
)

var (
	name    = "ltsvp"
	version = "0.4.2"

	flagset         = pflag.NewFlagSet(name, pflag.ContinueOnError)
	list            = flagset.StringP("keys", "k", "", "")
	outputDelimiter = flagset.StringP("output-delimiter", "D", "\t", "")
	remainLTSV      = flagset.BoolP("remain-ltsv", "r", false, "")
	isHelp          = flagset.BoolP("help", "h", false, "")
	isVersion       = flagset.BoolP("version", "v", false, "")
)

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s OPTION... [FILE]...
Print selected parts of LTSV from each FILE to standard output.

Options:
  -k, --keys=LIST
                 select only these keys (required)
  -D, --output-delimiter=STRING
                 use STRING to separate parts (default: \t)
  -r, --remain-ltsv
                 print selected parts as LTSV
  -h, --help
                 display this help text and exit
  -v, --version
                 output version information and exit

LIST is made up of keys separated by commas.
  host           # Select host
  host,time,ua   # Select host, time, and ua
`[1:], name)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func printErr(err interface{}) {
	fmt.Fprintf(os.Stderr, "%s: %s\n", name, err)
}

func guideToHelp() {
	fmt.Fprintf(os.Stderr, "Try '%s --help' for more information.\n", name)
}

func do(l *LTSVScanner) error {
	for l.Scan() {
		fmt.Println(l.Text())
	}
	return l.Err()
}

func main() {
	flagset.SetOutput(ioutil.Discard)
	if err := flagset.Parse(os.Args[1:]); err != nil {
		printErr(err)
		guideToHelp()
		os.Exit(2)
	}
	switch {
	case *isHelp:
		printUsage()
		os.Exit(0)
	case *isVersion:
		printVersion()
		os.Exit(0)
	}

	specifiedList := false
	flagset.Visit(func(f *pflag.Flag) {
		if f.Name == "keys" {
			specifiedList = true
		}
	})
	if !specifiedList {
		printErr("no specify LIST")
		guideToHelp()
		os.Exit(2)
	}
	keys := ParseKeysList(*list)

	r, err := argf.From(flagset.Args())
	if err != nil {
		printErr(err)
		guideToHelp()
		os.Exit(2)
	}

	l := NewLTSVScanner(keys, r)
	l.OutputDelimiter = *outputDelimiter
	l.RemainLTSV = *remainLTSV
	if err := do(l); err != nil {
		printErr(err)
		os.Exit(1)
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
	"net"

	"github.com/gaia-adm/pumba/action"
	"github.com/gaia-adm/pumba/container"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/johntdyer/slackrus"
)

var (
	wg      sync.WaitGroup
	client  container.Client
	cleanup bool
)

const (
	defaultKillSignal = "SIGKILL"
	re2prefix         = "re2:"
	release           = "v0.1.10"
	defaultNetemCmd   = "delay 1000ms"
)

type commandT struct {
	pattern string
	names   []string
	command string
	option string
}

func init() {
	log.SetLevel(log.InfoLevel)
	log.SetFormatter(&log.TextFormatter{})
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func main() {
	rootCertPath := "/etc/ssl/docker"

	if os.Getenv("DOCKER_CERT_PATH") != "" {
		rootCertPath = os.Getenv("DOCKER_CERT_PATH")
	}

	app := cli.NewApp()
	app.Name = "Pumba"
	app.Version = release
	app.Usage = "Pumba is a resiliency tool that helps applications tolerate random Docker container failures."
	app.Before = before
	app.Commands = []cli.Command{
		{
			Name: "run",
			Flags: []cli.Flag{
				cli.StringSliceFlag{
					Name:  "chaos, c",
					Usage: "chaos command: `container(s,)/re2:regex|interval(s/m/h postfix)|chaos_command(see above)`",
				},
				cli.BoolFlag{
					Name:        "random, r",
					Usage:       "Random mode: randomly select single matching container as a target for the specified chaos action",
					Destination: &actions.RandomMode,
				},
				cli.BoolFlag{
					Name:        "dry",
					Usage:       "enable 'dry run' mode: does not execute chaos action, just logs actions",
					Destination: &actions.DryMode,
				},
			},
			Usage: "Pumba starts making chaos: periodically (and randomly) affecting specified containers.",
			Description: "Ask Pumba to run periodically (and randomly) specified chaos_command on selected container(s).\n\n" +
				"   List of supported chaos_command(s):\n" +
				"    * STOP - stop running container(s)\n" +
				"    * KILL(:SIGNAL) - kill running container(s), optionally sending specified Linux SIGNAL (SIGKILL by default)\n" +
				"    * RM - force remove running container(s)\n" +
				"    * PAUSE:interval(ms/s/m/h postfix) - pause all processes within running container(s) for specified interval" +
				"    * DISRUPT(:netem command)(:target ip)",
			Action: run,
		},
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "host, H",
			Usage:  "daemon socket to connect to",
			Value:  "unix:///var/run/docker.sock",
			EnvVar: "DOCKER_HOST",
		},
		cli.BoolFlag{
			Name:  "tls",
			Usage: "use TLS; implied by --tlsverify",
		},
		cli.BoolFlag{
			Name:   "tlsverify",
			Usage:  "use TLS and verify the remote",
			EnvVar: "DOCKER_TLS_VERIFY",
		},
		cli.StringFlag{
			Name:  "tlscacert",
			Usage: "trust certs signed only by this CA",
			Value: fmt.Sprintf("%s/ca.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlscert",
			Usage: "client certificate for TLS authentication",
			Value: fmt.Sprintf("%s/cert.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlskey",
			Usage: "client key for TLS authentication",
			Value: fmt.Sprintf("%s/key.pem", rootCertPath),
		},
		cli.BoolFlag{
			Name:  "debug",
			Usage: "enable debug mode with verbose logging",
		},
		cli.BoolFlag{
			Name:  "json",
			Usage: "produce log in JSON format: Logstash and Splunk friendly"},
		cli.StringFlag{
			Name:  "slackhook",
			Usage: "Slack web hook url. Send Pumba log events to Slack",
		},
		cli.StringFlag{
			Name:  "slackchannel",
			Usage: "Slack channel for Pumba log events",
			Value: "#pumba",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func before(c *cli.Context) error {
	// set debug log level
	if c.GlobalBool("debug") {
		log.SetLevel(log.DebugLevel)
	}
	// set log formatter to JSON
	if c.GlobalBool("json") {
		log.SetFormatter(&log.JSONFormatter{})
	}
	// set Slack log channel
	if c.GlobalString("slackhook") != "" {
		log.AddHook(&slackrus.SlackrusHook{
			HookURL:        c.GlobalString("slackhook"),
			AcceptedLevels: slackrus.LevelThreshold(log.GetLevel()),
			Channel:        c.GlobalString("slackchannel"),
			IconEmoji:      ":boar:",
			Username:       "pumba_bot",
		})
	}

	cleanup = c.GlobalBool("cleanup")

	// Set-up container client
	tls, err := tlsConfig(c)
	if err != nil {
		return err
	}

	client = container.NewClient(c.GlobalString("host"), tls, !c.GlobalBool("no-pull"))

	handleSignals()
	return nil
}

func run(c *cli.Context) {
	if err := actions.CheckPrereqs(client, cleanup); err != nil {
		log.Fatal(err)
	}
	if err := createChaos(actions.Pumba{}, c.StringSlice("chaos"), 1, false); err != nil {
		log.Fatal(err)
	}
}

func createChaos(chaos actions.Chaos, args []string, limit int, test bool) error {
	// docker channel to pass all "stop" commands to
	dc := make(chan commandT)
	glimit := limit * len(args)

	// range over all chaos arguments
	for _, chaosArg := range args {
		s := strings.Split(chaosArg, "|")
		if len(s) != 3 {
			return errors.New("Unexpected format for chaos_arg: use | separated triple")
		}
		// get container name pattern
		var pattern string
		var names []string
		if strings.HasPrefix(s[0], re2prefix) {
			pattern = strings.Trim(s[0], re2prefix)
			log.Debugf("Pattern: '%s'", pattern)
		} else {
			if s[0] != "" {
				names = strings.Split(s[0], ",")
			}
			log.Debugf("Names: '%s'", names)
		}
		// get interval duration
		interval, err := time.ParseDuration(s[1])
		if err != nil {
			return err
		}
		log.Debugf("Interval: '%s'", interval.String())
		// get command and its option (if specified)
		cs := strings.Split(s[2], ":")
		command := strings.ToUpper(cs[0])
		if !stringInSlice(command, []string{"STOP", "KILL", "RM", "PAUSE", "DISRUPT"}) {
			return errors.New("Unexpected command in chaos option: can be STOP, KILL, RM, PAUSE or DISRUPT")
		}
		log.Debugf("Command: '%s'", command)
		// 2 actions upport a second argument: KILL/STOP:signal
		//	and DISRUPT:netem command:target ip
		// accordingly assign 2nd cmd line argument if exists
		option := defaultKillSignal
		if command == "DISRUPT" {
			option := defaultNetemCmd
		}
		if len(cs) >= 2 {
			option = cs[1]
			if command == "PAUSE" {
				log.Debugf("Pause interval: '%s'", option)
			} else if command == "STOP" || command == "KILL" {
				// convert signal to UPPER
				option := strings.ToUpper(option)
				log.Debugf("Signal: '%s'", option)
			} else if command == "DISRUPT" {
				option = defaultNetemCmd
				// the string may be netem command or target IP - as the user
				//  can omit the command part and use the default
				if len(cs) == 3 {
					// then we have both command and target IP, just need to put them
					//   together for the internal implementaion
					option = cs[1] + ":" + cs[2]
				} else {
					// If it's IP, re-concat it with the default command
					//  otherwise, just replace the netem command
					if net.ParseIP(cs[1]) != nil {
						option = option + ":" + cs[1]
					} else {
						option = cs[1]
					}
				}
				log.Debugf("Netem Command: '%s'", option)
			} else {
				log.Debugf("2nd argument doesn't correspond with command: '%s'", cs[1])
				return errors.New("Surplus 2nd argument to chaos action command")
			}
		}

		ticker := time.NewTicker(interval)
		go func(cmd commandT, limit int, test bool) {
			for range ticker.C {
				if limit > 0 {
					log.Debugf("Tick: '%s'", cmd)
					dc <- cmd
					if test {
						limit--
					}
				}
			}
		}(commandT{pattern, names, command, option}, limit, test)
	}
	for cmd := range dc {
		if test {
			glimit--
		}
		if glimit == 0 {
			break
		}
		wg.Add(1)
		go func(cmd commandT) {
			defer wg.Done()
			var err error
			switch cmd.command {
			case "STOP":
				if cmd.pattern == "" {
					err = chaos.StopByName(client, cmd.names)
				} else {
					err = chaos.StopByPattern(client, cmd.pattern)
				}
			case "KILL":
				if cmd.pattern == "" {
					err = chaos.KillByName(client, cmd.names, cmd.option)
				} else {
					err = chaos.KillByPattern(client, cmd.pattern, cmd.option)
				}
			case "RM":
				if cmd.pattern == "" {
					err = chaos.RemoveByName(client, cmd.names, true)
				} else {
					err = chaos.RemoveByPattern(client, cmd.pattern, true)
				}
			case "DISRUPT":
				if cmd.pattern == "" {
					err = chaos.DisruptByName(client, cmd.names, cmd.option)
				} else {
					err = chaos.DisruptByPattern(client, cmd.pattern,cmd.option)
				}
			case "PAUSE":
				if cmd.pattern == "" {
					err = chaos.PauseByName(client, cmd.names, cmd.option)
				} else {
					err = chaos.PauseByPattern(client, cmd.pattern, cmd.option)
				}
			}
			if err != nil {
				log.Error(err)
			}
		}(cmd)

	}
	return nil
}

func handleSignals() {
	// Graceful shut-down on SIGINT/SIGTERM
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)

	go func() {
		<-c
		wg.Wait()
		os.Exit(1)
	}()
}

// tlsConfig translates the command-line options into a tls.Config struct
func tlsConfig(c *cli.Context) (*tls.Config, error) {
	var tlsConfig *tls.Config
	var err error
	caCertFlag := c.GlobalString("tlscacert")
	certFlag := c.GlobalString("tlscert")
	keyFlag := c.GlobalString("tlskey")

	if c.GlobalBool("tls") || c.GlobalBool("tlsverify") {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: !c.GlobalBool("tlsverify"),
		}

		// Load CA cert
		if caCertFlag != "" {
			var caCert []byte

			if strings.HasPrefix(caCertFlag, "/") {
				caCert, err = ioutil.ReadFile(caCertFlag)
				if err != nil {
					return nil, err
				}
			} else {
				caCert = []byte(caCertFlag)
			}

			caCertPool := x509.NewCertPool()
			caCertPool.AppendCertsFromPEM(caCert)

			tlsConfig.RootCAs = caCertPool
		}

		// Load client certificate
		if certFlag != "" && keyFlag != "" {
			var cert tls.Certificate

			if strings.HasPrefix(certFlag, "/") && strings.HasPrefix(keyFlag, "/") {
				cert, err = tls.LoadX509KeyPair(certFlag, keyFlag)
				if err != nil {
					return nil, err
				}
			} else {
				cert, err = tls.X509KeyPair([]byte(certFlag), []byte(keyFlag))
				if err != nil {
					return nil, err
				}
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}
	}

	return tlsConfig, nil
}
<commit_msg>fix logical and syntax error from merge<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
	"net"

	"github.com/gaia-adm/pumba/action"
	"github.com/gaia-adm/pumba/container"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/johntdyer/slackrus"
)

var (
	wg      sync.WaitGroup
	client  container.Client
	cleanup bool
)

const (
	defaultKillSignal = "SIGKILL"
	re2prefix         = "re2:"
	release           = "v0.1.10"
	defaultNetemCmd   = "delay 1000ms"
)

type commandT struct {
	pattern string
	names   []string
	command string
	option string
}

func init() {
	log.SetLevel(log.InfoLevel)
	log.SetFormatter(&log.TextFormatter{})
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func main() {
	rootCertPath := "/etc/ssl/docker"

	if os.Getenv("DOCKER_CERT_PATH") != "" {
		rootCertPath = os.Getenv("DOCKER_CERT_PATH")
	}

	app := cli.NewApp()
	app.Name = "Pumba"
	app.Version = release
	app.Usage = "Pumba is a resiliency tool that helps applications tolerate random Docker container failures."
	app.Before = before
	app.Commands = []cli.Command{
		{
			Name: "run",
			Flags: []cli.Flag{
				cli.StringSliceFlag{
					Name:  "chaos, c",
					Usage: "chaos command: `container(s,)/re2:regex|interval(s/m/h postfix)|chaos_command(see above)`",
				},
				cli.BoolFlag{
					Name:        "random, r",
					Usage:       "Random mode: randomly select single matching container as a target for the specified chaos action",
					Destination: &actions.RandomMode,
				},
				cli.BoolFlag{
					Name:        "dry",
					Usage:       "enable 'dry run' mode: does not execute chaos action, just logs actions",
					Destination: &actions.DryMode,
				},
			},
			Usage: "Pumba starts making chaos: periodically (and randomly) affecting specified containers.",
			Description: "Ask Pumba to run periodically (and randomly) specified chaos_command on selected container(s).\n\n" +
				"   List of supported chaos_command(s):\n" +
				"    * STOP - stop running container(s)\n" +
				"    * KILL(:SIGNAL) - kill running container(s), optionally sending specified Linux SIGNAL (SIGKILL by default)\n" +
				"    * RM - force remove running container(s)\n" +
				"    * PAUSE:interval(ms/s/m/h postfix) - pause all processes within running container(s) for specified interval" +
				"    * DISRUPT(:netem command)(:target ip)",
			Action: run,
		},
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "host, H",
			Usage:  "daemon socket to connect to",
			Value:  "unix:///var/run/docker.sock",
			EnvVar: "DOCKER_HOST",
		},
		cli.BoolFlag{
			Name:  "tls",
			Usage: "use TLS; implied by --tlsverify",
		},
		cli.BoolFlag{
			Name:   "tlsverify",
			Usage:  "use TLS and verify the remote",
			EnvVar: "DOCKER_TLS_VERIFY",
		},
		cli.StringFlag{
			Name:  "tlscacert",
			Usage: "trust certs signed only by this CA",
			Value: fmt.Sprintf("%s/ca.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlscert",
			Usage: "client certificate for TLS authentication",
			Value: fmt.Sprintf("%s/cert.pem", rootCertPath),
		},
		cli.StringFlag{
			Name:  "tlskey",
			Usage: "client key for TLS authentication",
			Value: fmt.Sprintf("%s/key.pem", rootCertPath),
		},
		cli.BoolFlag{
			Name:  "debug",
			Usage: "enable debug mode with verbose logging",
		},
		cli.BoolFlag{
			Name:  "json",
			Usage: "produce log in JSON format: Logstash and Splunk friendly"},
		cli.StringFlag{
			Name:  "slackhook",
			Usage: "Slack web hook url. Send Pumba log events to Slack",
		},
		cli.StringFlag{
			Name:  "slackchannel",
			Usage: "Slack channel for Pumba log events",
			Value: "#pumba",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func before(c *cli.Context) error {
	// set debug log level
	if c.GlobalBool("debug") {
		log.SetLevel(log.DebugLevel)
	}
	// set log formatter to JSON
	if c.GlobalBool("json") {
		log.SetFormatter(&log.JSONFormatter{})
	}
	// set Slack log channel
	if c.GlobalString("slackhook") != "" {
		log.AddHook(&slackrus.SlackrusHook{
			HookURL:        c.GlobalString("slackhook"),
			AcceptedLevels: slackrus.LevelThreshold(log.GetLevel()),
			Channel:        c.GlobalString("slackchannel"),
			IconEmoji:      ":boar:",
			Username:       "pumba_bot",
		})
	}

	cleanup = c.GlobalBool("cleanup")

	// Set-up container client
	tls, err := tlsConfig(c)
	if err != nil {
		return err
	}

	client = container.NewClient(c.GlobalString("host"), tls, !c.GlobalBool("no-pull"))

	handleSignals()
	return nil
}

func run(c *cli.Context) {
	if err := actions.CheckPrereqs(client, cleanup); err != nil {
		log.Fatal(err)
	}
	if err := createChaos(actions.Pumba{}, c.StringSlice("chaos"), 1, false); err != nil {
		log.Fatal(err)
	}
}

func createChaos(chaos actions.Chaos, args []string, limit int, test bool) error {
	// docker channel to pass all "stop" commands to
	dc := make(chan commandT)
	glimit := limit * len(args)

	// range over all chaos arguments
	for _, chaosArg := range args {
		s := strings.Split(chaosArg, "|")
		if len(s) != 3 {
			return errors.New("Unexpected format for chaos_arg: use | separated triple")
		}
		// get container name pattern
		var pattern string
		var names []string
		if strings.HasPrefix(s[0], re2prefix) {
			pattern = strings.Trim(s[0], re2prefix)
			log.Debugf("Pattern: '%s'", pattern)
		} else {
			if s[0] != "" {
				names = strings.Split(s[0], ",")
			}
			log.Debugf("Names: '%s'", names)
		}
		// get interval duration
		interval, err := time.ParseDuration(s[1])
		if err != nil {
			return err
		}
		log.Debugf("Interval: '%s'", interval.String())
		// get command and its option (if specified)
		cs := strings.Split(s[2], ":")
		command := strings.ToUpper(cs[0])
		if !stringInSlice(command, []string{"STOP", "KILL", "RM", "PAUSE", "DISRUPT"}) {
			return errors.New("Unexpected command in chaos option: can be STOP, KILL, RM, PAUSE or DISRUPT")
		}
		log.Debugf("Command: '%s'", command)
		// 2 actions upport a second argument: KILL/STOP:signal
		//	and DISRUPT:netem command:target ip
		// accordingly assign 2nd cmd line argument if exists
		option := defaultKillSignal
		if command == "DISRUPT" {
			option = defaultNetemCmd
		}
		if len(cs) >= 2 {
			option = cs[1]
			if command == "PAUSE" {
				log.Debugf("Pause interval: '%s'", option)
			} else if command == "STOP" || command == "KILL" {
				// convert signal to UPPER
				option := strings.ToUpper(option)
				log.Debugf("Signal: '%s'", option)
			} else if command == "DISRUPT" {
				option = defaultNetemCmd
				// the string may be netem command or target IP - as the user
				//  can omit the command part and use the default
				if len(cs) == 3 {
					// then we have both command and target IP, just need to put them
					//   together for the internal implementaion
					option = cs[1] + ":" + cs[2]
				} else {
					// If it's IP, re-concat it with the default command
					//  otherwise, just replace the netem command
					if net.ParseIP(cs[1]) != nil {
						option = option + ":" + cs[1]
					} else {
						option = cs[1]
					}
				}
				log.Debugf("Netem Command: '%s'", option)
			} else {
				log.Debugf("2nd argument doesn't correspond with command: '%s'", cs[1])
				return errors.New("Surplus 2nd argument to chaos action command")
			}
		}

		ticker := time.NewTicker(interval)
		go func(cmd commandT, limit int, test bool) {
			for range ticker.C {
				if limit > 0 {
					log.Debugf("Tick: '%s'", cmd)
					dc <- cmd
					if test {
						limit--
					}
				}
			}
		}(commandT{pattern, names, command, option}, limit, test)
	}
	for cmd := range dc {
		if test {
			glimit--
		}
		if glimit == 0 {
			break
		}
		wg.Add(1)
		go func(cmd commandT) {
			defer wg.Done()
			var err error
			switch cmd.command {
			case "STOP":
				if cmd.pattern == "" {
					err = chaos.StopByName(client, cmd.names)
				} else {
					err = chaos.StopByPattern(client, cmd.pattern)
				}
			case "KILL":
				if cmd.pattern == "" {
					err = chaos.KillByName(client, cmd.names, cmd.option)
				} else {
					err = chaos.KillByPattern(client, cmd.pattern, cmd.option)
				}
			case "RM":
				if cmd.pattern == "" {
					err = chaos.RemoveByName(client, cmd.names, true)
				} else {
					err = chaos.RemoveByPattern(client, cmd.pattern, true)
				}
			case "DISRUPT":
				if cmd.pattern == "" {
					err = chaos.DisruptByName(client, cmd.names, cmd.option)
				} else {
					err = chaos.DisruptByPattern(client, cmd.pattern,cmd.option)
				}
			case "PAUSE":
				if cmd.pattern == "" {
					err = chaos.PauseByName(client, cmd.names, cmd.option)
				} else {
					err = chaos.PauseByPattern(client, cmd.pattern, cmd.option)
				}
			}
			if err != nil {
				log.Error(err)
			}
		}(cmd)

	}
	return nil
}

func handleSignals() {
	// Graceful shut-down on SIGINT/SIGTERM
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)

	go func() {
		<-c
		wg.Wait()
		os.Exit(1)
	}()
}

// tlsConfig translates the command-line options into a tls.Config struct
func tlsConfig(c *cli.Context) (*tls.Config, error) {
	var tlsConfig *tls.Config
	var err error
	caCertFlag := c.GlobalString("tlscacert")
	certFlag := c.GlobalString("tlscert")
	keyFlag := c.GlobalString("tlskey")

	if c.GlobalBool("tls") || c.GlobalBool("tlsverify") {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: !c.GlobalBool("tlsverify"),
		}

		// Load CA cert
		if caCertFlag != "" {
			var caCert []byte

			if strings.HasPrefix(caCertFlag, "/") {
				caCert, err = ioutil.ReadFile(caCertFlag)
				if err != nil {
					return nil, err
				}
			} else {
				caCert = []byte(caCertFlag)
			}

			caCertPool := x509.NewCertPool()
			caCertPool.AppendCertsFromPEM(caCert)

			tlsConfig.RootCAs = caCertPool
		}

		// Load client certificate
		if certFlag != "" && keyFlag != "" {
			var cert tls.Certificate

			if strings.HasPrefix(certFlag, "/") && strings.HasPrefix(keyFlag, "/") {
				cert, err = tls.LoadX509KeyPair(certFlag, keyFlag)
				if err != nil {
					return nil, err
				}
			} else {
				cert, err = tls.X509KeyPair([]byte(certFlag), []byte(keyFlag))
				if err != nil {
					return nil, err
				}
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}
	}

	return tlsConfig, nil
}
<|endoftext|>