<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/coreos/etcd/client"
	"github.com/rjeczalik/notify"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
	"golang.org/x/net/context"
	"flag"
)

const MARK_FILE_NAME = ".ETCDIR_MARK_FILE_HUGSDBDND" // Name of lock-file for prevent bad things
const DEFAULT_DIRMODE = 0777
const DEFAULT_FILEMODE = 0777
const EVENT_CHANNEL_LEN = 1000
const LOCK_INTERVAL = time.Second // Wait since previous touch to can get lock directory once more.

var (
	serverAddr = flag.String("server", "http://localhost:2379", "Client url for one of cluster servers")
)

type fileChangeEvent struct {
	Path      string
	IsDir     bool
	IsRemoved bool
	Content   []byte
}

/*
Monitoring changes in etcd server.
It designed for run in separate goroutine.
 */
func etcdMon(etcdRootPath string, config client.Config, bus chan fileChangeEvent, startIndex uint64) {
	c, err := client.New(config)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(c)
	var nextEvent uint64 = startIndex
	for {
		response, err := kapi.Watcher(etcdRootPath, &client.WatcherOptions{AfterIndex: nextEvent, Recursive: true}).Next(context.Background())
		if err != nil {
			log.Println(err)
			time.Sleep(time.Second)
			continue
		}
		nextEvent = response.Index
		if response.Action == "delete" {
			bus <- fileChangeEvent{Path: response.Node.Key, IsRemoved: true, IsDir: response.Node.Dir}
			continue
		}
		if response.Node.Dir {
			bus <- fileChangeEvent{Path: response.Node.Key, IsDir: response.Node.Dir}
			continue
		}
		bus <- fileChangeEvent{Path: response.Node.Key, Content: []byte(response.Node.Value)}
	}
}

/*
Monitoring changes in file system.
It designed for run in separate goroutine.
 */
func fileMon(path string, bus chan fileChangeEvent) {
	// Make the channel buffered to ensure no event is dropped. Notify will drop
	// an event if the receiver is not able to keep up the sending pace.
	c := make(chan notify.EventInfo, 1)

	// Set up a watchpoint listening on events within current working directory.
	// Dispatch each create and remove events separately to c.
	if err := notify.Watch(path+"/...", c, notify.All); err != nil {
		log.Fatal(err)
	}
	defer notify.Stop(c)

	// Block until an event is received.
	for {
		event := <-c
		fstat, err := os.Lstat(event.Path())
		if os.IsNotExist(err) {
			bus <- fileChangeEvent{Path: event.Path(), IsRemoved: true}
			continue
		}

		if err != nil {
			log.Println(err)
			continue
		}

		if fstat.IsDir() {
			bus <- fileChangeEvent{Path: event.Path(), IsDir: true}
			continue
		}

		content, err := ioutil.ReadFile(event.Path())
		if err != nil {
			log.Println(err)
		}
		bus <- fileChangeEvent{Path: event.Path(), Content: content}
	}
}

/*
Clear dir and dump content of etcd to the dir.
ATTENTION: the function REMOVE ALL CONTENT of the dir.
 */
func firstSyncEtcDir(etcdRootPath string, etcdConfig client.Config, dir string) (etcdIndex uint64) {
	dirFile, err := os.Open(dir)
	if err != nil {
		panic(err)
	}
	dirNames, err := dirFile.Readdirnames(-1)
	if err != nil {
		panic(err)
	}
	for _, item := range dirNames {
		err = os.RemoveAll(filepath.Join(dir, item))
		if err != nil {
			log.Println("I can't remove: ", filepath.Join(dir, item))
			panic(err)
		}
	}
	err = ioutil.WriteFile(filepath.Join(dir, MARK_FILE_NAME), []byte{}, DEFAULT_FILEMODE)
	if err != nil {
		log.Println("I can't create touchfile: ", filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		log.Println("Can't create etcdClient: ", err)
		panic(err)
	}

	kapi := client.NewKeysAPI(etcdClient)
	response, err := kapi.Get(context.Background(), etcdRootPath, &client.GetOptions{Recursive: true, Quorum: true})
	if err != nil {
		fmt.Println("I can't get initial etcd state: ", err)
		panic(err)
	}
	writeNodeToDir(dir, etcdRootPath, response.Node)
	etcdIndex = response.Index
	return
}

/*
Check if dir can be locked and lock it.
Return true if lock succesfully.
 */
func lock(dir string)bool{
	lockFile := filepath.Join(dir, MARK_FILE_NAME)
	stat, err := os.Stat(lockFile)
	if err != nil {
		log.Println("Can't stat lock file: ", lockFile, err)
		return false
	}
	if time.Now().Sub(stat.ModTime()) <= LOCK_INTERVAL {
		return false
	}

	pid := os.Getpid()
	go func(){
		for {
			mess := fmt.Sprint("PID: ", pid, "\nLAST TIME: ", time.Now().String())
			ioutil.WriteFile(lockFile, []byte(mess), DEFAULT_FILEMODE)
			time.Sleep(LOCK_INTERVAL / 3)
		}
	}()
	return true
}

func main() {
	flag.Usage = printUsage
	flag.Parse()
	if flag.NArg() != 1 {
		printUsage()
		return
	}

	dir, err := filepath.Abs(flag.Arg(0))
	if err != nil {
		panic(err)
	}
	dirStat, err := os.Stat(dir)
	if err != nil {
		panic(err)
	}
	if !dirStat.IsDir() {
		fmt.Printf("'%v' is not dir.\n", dir)
		return
	}

	_, err = os.Stat(filepath.Join(dir, MARK_FILE_NAME))
	if os.IsNotExist(err) {
		fmt.Printf(`You have to create file '%[1]v' before usage dir as syncdir. You can do it by command:
echo > %[1]v
`, filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	if !lock(dir){
		log.Println("Can't get lock. May be another instance work with the dir")
		return
	}

	etcdRootPath := "/"
	if len(os.Args) > 3 {
		etcdRootPath = os.Args[3]
	}

	fmt.Println(os.Args)
	etcdConfig := client.Config{Endpoints: []string{*serverAddr}}
	fmt.Println(etcdConfig)
	etcdStartFrom := firstSyncEtcDir(etcdRootPath, etcdConfig, dir)

	etcdChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)
	fsChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)

	go fileMon(dir, fsChan)
	go etcdMon(etcdRootPath, etcdConfig, etcdChan, etcdStartFrom)

	syncProcess(dir, etcdRootPath, etcdConfig, etcdChan, fsChan)
}

func printUsage() {
	fmt.Printf(`%v [options] <syncdir>
syncdir - directory for show etcd content. ALL CURRENT CONTENT WILL BE LOST.
you have to create file '%v' in syncdir before can use it.
`, os.Args[0], MARK_FILE_NAME)
	flag.PrintDefaults()
}

/*
function for replicate changes between etcd and file system.
It is never returned function.
It can be run in separate goroutine or call it as last function in main()
 */
func syncProcess(dir, etcdRootDir string, etcdConfig client.Config, etcdChan, fsChan <-chan fileChangeEvent) {
	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(etcdClient)
	ctx := context.Background()
	fsMarkFile := filepath.Join(dir, MARK_FILE_NAME)
	for {
		var event fileChangeEvent
		select {
		case event = <-etcdChan:
			fsPath := filepath.Join(dir, event.Path)
			if fsPath == fsMarkFile {
				continue
			}
			switch {
			case event.IsRemoved:
				err := os.RemoveAll(fsPath)
				if err != nil {
					log.Println("Can't remove: ", fsPath, err)
				}
			case event.IsDir:
				err := os.Mkdir(fsPath, DEFAULT_DIRMODE)
				if err != nil && !os.IsExist(err){
					log.Println("Can't make dir: ", fsPath, err)
				}
			default:
				if content, err := ioutil.ReadFile(fsPath); err == nil {
					if bytes.Equal(content, event.Content) {
						// Skip if contents are equals
						continue
					}
				}
				err := ioutil.WriteFile(fsPath, event.Content, DEFAULT_FILEMODE)
				if err != nil {
					log.Println("Can't write file: ", fsPath, err)
				}
			}
		case event = <-fsChan:
			if event.Path == fsMarkFile {
				continue
			}
			etcdPath := etcdRootDir + event.Path[len(dir):]
			etcdPath = strings.Replace(etcdPath, "\\", "/", -1)
			switch {
			case event.IsRemoved:
				_, err := kapi.Delete(ctx, etcdPath, &client.DeleteOptions{Recursive: true})
				if err != nil {
					log.Println("Can't remove etcd: "+etcdPath, err)
				}
			case event.IsDir:
				_, err := kapi.Set(ctx, etcdPath, "", &client.SetOptions{Dir: true})
				if err != nil {
					log.Println("Can't create etcd dir: ", etcdPath, err)
				}
			default:
				if resp, err := kapi.Get(ctx, etcdPath, &client.GetOptions{Quorum: true}); err == nil {
					if bytes.Equal([]byte(resp.Node.Value), event.Content) {
						// Skip equal contents
						continue
					}
				}
				_, err := kapi.Set(ctx, etcdPath, string(event.Content), nil)
				if err != nil {
					log.Println("Can't set etcd value: ", etcdPath, err)
				}
			}
		}
	}
}

func writeNodeToDir(dir, root string, node *client.Node) {
	//log.Println("I can't create dir:  ", node.Key," ", dir, "root:", root)
	nodePath := filepath.Join(dir, node.Key[len(root)-1:])
	//log.Println("I can't create dir: ", nodePath, " ", node.Key," ", dir, " ", root)
	if node.Dir {
		err := os.Mkdir(nodePath, DEFAULT_DIRMODE)
		if err != nil && !os.IsExist(err) {
			log.Println("I can't create dir: ", nodePath, " ", node.Key," ", dir, " ", root)
			panic(err)
		}
		for _, item := range node.Nodes {
			writeNodeToDir(dir, root, item)
		}
	} else {
		err := ioutil.WriteFile(nodePath, []byte(node.Value), DEFAULT_FILEMODE)
		if err != nil {
			log.Println("I can't create file: ", nodePath)
			panic(err)
		}
	}
}
<commit_msg>serverRootDir parameter<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/coreos/etcd/client"
	"github.com/rjeczalik/notify"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
	"golang.org/x/net/context"
	"flag"
)

const MARK_FILE_NAME = ".ETCDIR_MARK_FILE_HUGSDBDND" // Name of lock-file for prevent bad things
const DEFAULT_DIRMODE = 0777
const DEFAULT_FILEMODE = 0777
const EVENT_CHANNEL_LEN = 1000
const LOCK_INTERVAL = time.Second // Wait since previous touch to can get lock directory once more.

var (
	serverAddr = flag.String("server", "http://localhost:2379", "Client url for one of cluster servers")
	serverRootDir = flag.String("root", "/", "server root dir for map")
)

type fileChangeEvent struct {
	Path      string
	IsDir     bool
	IsRemoved bool
	Content   []byte
}

/*
Monitoring changes in etcd server.
It designed for run in separate goroutine.
 */
func etcdMon(etcdRootPath string, config client.Config, bus chan fileChangeEvent, startIndex uint64) {
	c, err := client.New(config)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(c)
	var nextEvent uint64 = startIndex
	for {
		response, err := kapi.Watcher(etcdRootPath, &client.WatcherOptions{AfterIndex: nextEvent, Recursive: true}).Next(context.Background())
		if err != nil {
			log.Println(err)
			time.Sleep(time.Second)
			continue
		}
		nextEvent = response.Index
		if response.Action == "delete" {
			bus <- fileChangeEvent{Path: response.Node.Key, IsRemoved: true, IsDir: response.Node.Dir}
			continue
		}
		if response.Node.Dir {
			bus <- fileChangeEvent{Path: response.Node.Key, IsDir: response.Node.Dir}
			continue
		}
		bus <- fileChangeEvent{Path: response.Node.Key, Content: []byte(response.Node.Value)}
	}
}

/*
Monitoring changes in file system.
It designed for run in separate goroutine.
 */
func fileMon(path string, bus chan fileChangeEvent) {
	// Make the channel buffered to ensure no event is dropped. Notify will drop
	// an event if the receiver is not able to keep up the sending pace.
	c := make(chan notify.EventInfo, 1)

	// Set up a watchpoint listening on events within current working directory.
	// Dispatch each create and remove events separately to c.
	if err := notify.Watch(path+"/...", c, notify.All); err != nil {
		log.Fatal(err)
	}
	defer notify.Stop(c)

	// Block until an event is received.
	for {
		event := <-c
		fstat, err := os.Lstat(event.Path())
		if os.IsNotExist(err) {
			bus <- fileChangeEvent{Path: event.Path(), IsRemoved: true}
			continue
		}

		if err != nil {
			log.Println(err)
			continue
		}

		if fstat.IsDir() {
			bus <- fileChangeEvent{Path: event.Path(), IsDir: true}
			continue
		}

		content, err := ioutil.ReadFile(event.Path())
		if err != nil {
			log.Println(err)
		}
		bus <- fileChangeEvent{Path: event.Path(), Content: content}
	}
}

/*
Clear dir and dump content of etcd to the dir.
ATTENTION: the function REMOVE ALL CONTENT of the dir.
 */
func firstSyncEtcDir(etcdRootPath string, etcdConfig client.Config, dir string) (etcdIndex uint64) {
	dirFile, err := os.Open(dir)
	if err != nil {
		panic(err)
	}
	dirNames, err := dirFile.Readdirnames(-1)
	if err != nil {
		panic(err)
	}
	for _, item := range dirNames {
		err = os.RemoveAll(filepath.Join(dir, item))
		if err != nil {
			log.Println("I can't remove: ", filepath.Join(dir, item))
			panic(err)
		}
	}
	err = ioutil.WriteFile(filepath.Join(dir, MARK_FILE_NAME), []byte{}, DEFAULT_FILEMODE)
	if err != nil {
		log.Println("I can't create touchfile: ", filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		log.Println("Can't create etcdClient: ", err)
		panic(err)
	}

	kapi := client.NewKeysAPI(etcdClient)
	response, err := kapi.Get(context.Background(), etcdRootPath, &client.GetOptions{Recursive: true, Quorum: true})
	if err != nil {
		fmt.Println("I can't get initial etcd state: ", err)
		panic(err)
	}
	writeNodeToDir(dir, etcdRootPath, response.Node)
	etcdIndex = response.Index
	return
}

/*
Check if dir can be locked and lock it.
Return true if lock succesfully.
 */
func lock(dir string)bool{
	lockFile := filepath.Join(dir, MARK_FILE_NAME)
	stat, err := os.Stat(lockFile)
	if err != nil {
		log.Println("Can't stat lock file: ", lockFile, err)
		return false
	}
	if time.Now().Sub(stat.ModTime()) <= LOCK_INTERVAL {
		return false
	}

	pid := os.Getpid()
	go func(){
		for {
			mess := fmt.Sprint("PID: ", pid, "\nLAST TIME: ", time.Now().String())
			ioutil.WriteFile(lockFile, []byte(mess), DEFAULT_FILEMODE)
			time.Sleep(LOCK_INTERVAL / 3)
		}
	}()
	return true
}

func main() {
	flag.Usage = printUsage
	flag.Parse()
	if flag.NArg() != 1 {
		printUsage()
		return
	}

	dir, err := filepath.Abs(flag.Arg(0))
	if err != nil {
		panic(err)
	}
	dirStat, err := os.Stat(dir)
	if err != nil {
		panic(err)
	}
	if !dirStat.IsDir() {
		fmt.Printf("'%v' is not dir.\n", dir)
		return
	}

	_, err = os.Stat(filepath.Join(dir, MARK_FILE_NAME))
	if os.IsNotExist(err) {
		fmt.Printf(`You have to create file '%[1]v' before usage dir as syncdir. You can do it by command:
echo > %[1]v
`, filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	if !lock(dir){
		log.Println("Can't get lock. May be another instance work with the dir")
		return
	}

	fmt.Println(os.Args)
	etcdConfig := client.Config{Endpoints: []string{*serverAddr}}
	fmt.Println(etcdConfig)
	etcdStartFrom := firstSyncEtcDir(*serverRootDir, etcdConfig, dir)

	etcdChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)
	fsChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)

	go fileMon(dir, fsChan)
	go etcdMon(*serverRootDir, etcdConfig, etcdChan, etcdStartFrom)

	syncProcess(dir, *serverRootDir, etcdConfig, etcdChan, fsChan)
}

func printUsage() {
	fmt.Printf(`%v [options] <syncdir>
syncdir - directory for show etcd content. ALL CURRENT CONTENT WILL BE LOST.
you have to create file '%v' in syncdir before can use it.
`, os.Args[0], MARK_FILE_NAME)
	flag.PrintDefaults()
}

/*
function for replicate changes between etcd and file system.
It is never returned function.
It can be run in separate goroutine or call it as last function in main()
 */
func syncProcess(dir, etcdRootDir string, etcdConfig client.Config, etcdChan, fsChan <-chan fileChangeEvent) {
	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(etcdClient)
	ctx := context.Background()
	fsMarkFile := filepath.Join(dir, MARK_FILE_NAME)
	for {
		var event fileChangeEvent
		select {
		case event = <-etcdChan:
			fsPath := filepath.Join(dir, event.Path)
			if fsPath == fsMarkFile {
				continue
			}
			switch {
			case event.IsRemoved:
				err := os.RemoveAll(fsPath)
				if err != nil {
					log.Println("Can't remove: ", fsPath, err)
				}
			case event.IsDir:
				err := os.Mkdir(fsPath, DEFAULT_DIRMODE)
				if err != nil && !os.IsExist(err){
					log.Println("Can't make dir: ", fsPath, err)
				}
			default:
				if content, err := ioutil.ReadFile(fsPath); err == nil {
					if bytes.Equal(content, event.Content) {
						// Skip if contents are equals
						continue
					}
				}
				err := ioutil.WriteFile(fsPath, event.Content, DEFAULT_FILEMODE)
				if err != nil {
					log.Println("Can't write file: ", fsPath, err)
				}
			}
		case event = <-fsChan:
			if event.Path == fsMarkFile {
				continue
			}
			etcdPath := etcdRootDir + event.Path[len(dir):]
			etcdPath = strings.Replace(etcdPath, "\\", "/", -1)
			switch {
			case event.IsRemoved:
				_, err := kapi.Delete(ctx, etcdPath, &client.DeleteOptions{Recursive: true})
				if err != nil {
					log.Println("Can't remove etcd: "+etcdPath, err)
				}
			case event.IsDir:
				_, err := kapi.Set(ctx, etcdPath, "", &client.SetOptions{Dir: true})
				if err != nil {
					log.Println("Can't create etcd dir: ", etcdPath, err)
				}
			default:
				if resp, err := kapi.Get(ctx, etcdPath, &client.GetOptions{Quorum: true}); err == nil {
					if bytes.Equal([]byte(resp.Node.Value), event.Content) {
						// Skip equal contents
						continue
					}
				}
				_, err := kapi.Set(ctx, etcdPath, string(event.Content), nil)
				if err != nil {
					log.Println("Can't set etcd value: ", etcdPath, err)
				}
			}
		}
	}
}

func writeNodeToDir(dir, root string, node *client.Node) {
	//log.Println("I can't create dir:  ", node.Key," ", dir, "root:", root)
	nodePath := filepath.Join(dir, node.Key[len(root)-1:])
	//log.Println("I can't create dir: ", nodePath, " ", node.Key," ", dir, " ", root)
	if node.Dir {
		err := os.Mkdir(nodePath, DEFAULT_DIRMODE)
		if err != nil && !os.IsExist(err) {
			log.Println("I can't create dir: ", nodePath, " ", node.Key," ", dir, " ", root)
			panic(err)
		}
		for _, item := range node.Nodes {
			writeNodeToDir(dir, root, item)
		}
	} else {
		err := ioutil.WriteFile(nodePath, []byte(node.Value), DEFAULT_FILEMODE)
		if err != nil {
			log.Println("I can't create file: ", nodePath)
			panic(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/pprof"
	"os"
	"path/filepath"
	"runtime"

	"github.com/julienschmidt/httprouter"
	"github.com/tokubai/kinu/engine"
	"github.com/tokubai/kinu/logger"
	"github.com/vincent-petithory/dataurl"
	"github.com/zenazn/goji/bind"
	"github.com/zenazn/goji/graceful"
)

const (
	DEFAULT_BIND = "127.0.0.1:8080"
	VERSION      = "1.0.0.alpha9"
)

var (
	ErrInvalidImageExt = errors.New("supported image type is only jpg/jpeg")
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	engine.Initialize()
	defer engine.Finalize()

	router := httprouter.New()

	if os.Getenv("KINU_DEBUG") == "1" {
		router.GET("/debug/pprof/*pprof", HandlePprof)
	}

	router.GET("/images/:type/:geometry/:filename", GetImageHandler)

	router.POST("/upload", UploadImageHandler)
	router.POST("/sandbox", UploadImageToSandboxHandler)
	router.POST("/sandbox/attach", ApplyFromSandboxHandler)

	router.GET("/version", VersionHandler)
	router.GET("/worker/stats", WorkerStatsHandler)

	addr := os.Getenv("KINU_BIND")
	if len(addr) == 0 {
		addr = DEFAULT_BIND
	}

	graceful.HandleSignals()
	graceful.PreHook(func() {
		logger.Info("kinu received graceful shutdown signal.")
	})
	graceful.PostHook(func() {
		logger.Info("kinu stopped.")
	})

	logger.Info(fmt.Sprintf("starting kinu on %s", addr))

	err := graceful.Serve(bind.Socket(addr), router)
	if err != nil {
		logger.Panic(err)
	}

	graceful.Wait()
}

func HandlePprof(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	switch p.ByName("pprof") {
	case "/cmdline":
		pprof.Cmdline(w, r)
	case "/profile":
		pprof.Profile(w, r)
	case "/symbol":
		pprof.Symbol(w, r)
	default:
		pprof.Index(w, r)
	}
}

func SetContentType(w http.ResponseWriter, filename string) error {
	ext := ExtractExtension(filepath.Ext(filename))
	switch ext {
	case "jpg", "jpeg":
		w.Header().Set("Content-Type", "image/jpg")
		return nil
	case "png":
		w.Header().Set("Content-Type", "image/png")
		return nil
	case "gif":
		w.Header().Set("Content-Type", "image/gif")
		return nil
	case "data":
		w.Header().Set("Content-Type", "text/plain")
		return nil
	case "webp":
		w.Header().Set("Content-Type", "image/webp")
		return nil
	default:
		return ErrInvalidImageExt
	}
}

func RespondBadRequest(w http.ResponseWriter, reason string) {
	w.Header().Set("X-Kinu-BadRequest-Reason", reason)
	w.WriteHeader(http.StatusBadRequest)
}

func RespondNotFound(w http.ResponseWriter) {
	w.WriteHeader(http.StatusNotFound)
}

func RespondInternalServerError(w http.ResponseWriter, err error) {
	logger.ErrorDebug(err)
	w.WriteHeader(http.StatusInternalServerError)
}

func RespondServiceUnavailable(w http.ResponseWriter, err error) {
	logger.ErrorDebug(err)
	w.WriteHeader(http.StatusServiceUnavailable)
}

type UploadResult struct {
	ImageType string `json:"name"`
	ImageId   string `json:"id"`
}

func RespondImageUploadSuccessJson(w http.ResponseWriter, imageType string, imageId string) {
	json, err := json.Marshal(&UploadResult{ImageType: imageType, ImageId: imageId})
	if err != nil {
		RespondInternalServerError(w, err)
	}
	RespondJson(w, json)
}

func RespondJson(w http.ResponseWriter, json []byte) {
	w.Write(json)
}

func RespondImage(w http.ResponseWriter, bytes []byte) {
	w.Write(bytes)
}

func RespondDataURI(w http.ResponseWriter, bytes []byte) {
	io.WriteString(w, dataurl.EncodeBytes(bytes))
}
<commit_msg>Support sentry error tracking<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/pprof"
	"os"
	"path/filepath"
	"runtime"

	"github.com/getsentry/raven-go"

	"github.com/julienschmidt/httprouter"
	"github.com/tokubai/kinu/engine"
	"github.com/tokubai/kinu/logger"
	"github.com/vincent-petithory/dataurl"
	"github.com/zenazn/goji/bind"
	"github.com/zenazn/goji/graceful"
)

const (
	DEFAULT_BIND = "127.0.0.1:8080"
	VERSION      = "1.0.0.alpha9"
)

var (
	ErrInvalidImageExt = errors.New("supported image type is only jpg/jpeg")
	UseSentry          = false
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	engine.Initialize()
	defer engine.Finalize()

	router := httprouter.New()

	if os.Getenv("KINU_DEBUG") == "1" {
		router.GET("/debug/pprof/*pprof", HandlePprof)
	}

	ravenDSN := os.Getenv("KINU_SENTRY_DSN")
	if len(ravenDSN) != 0 {
		UseSentry = true
		raven.SetDSN(ravenDSN)
	}

	router.GET("/images/:type/:geometry/:filename", GetImageHandler)

	router.POST("/upload", UploadImageHandler)
	router.POST("/sandbox", UploadImageToSandboxHandler)
	router.POST("/sandbox/attach", ApplyFromSandboxHandler)

	router.GET("/version", VersionHandler)
	router.GET("/worker/stats", WorkerStatsHandler)

	addr := os.Getenv("KINU_BIND")
	if len(addr) == 0 {
		addr = DEFAULT_BIND
	}

	graceful.HandleSignals()
	graceful.PreHook(func() {
		logger.Info("kinu received graceful shutdown signal.")
	})
	graceful.PostHook(func() {
		logger.Info("kinu stopped.")
	})

	logger.Info(fmt.Sprintf("starting kinu on %s", addr))

	err := graceful.Serve(bind.Socket(addr), router)
	if err != nil {
		logger.Panic(err)
	}

	graceful.Wait()
}

func HandlePprof(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	switch p.ByName("pprof") {
	case "/cmdline":
		pprof.Cmdline(w, r)
	case "/profile":
		pprof.Profile(w, r)
	case "/symbol":
		pprof.Symbol(w, r)
	default:
		pprof.Index(w, r)
	}
}

func SetContentType(w http.ResponseWriter, filename string) error {
	ext := ExtractExtension(filepath.Ext(filename))
	switch ext {
	case "jpg", "jpeg":
		w.Header().Set("Content-Type", "image/jpg")
		return nil
	case "png":
		w.Header().Set("Content-Type", "image/png")
		return nil
	case "gif":
		w.Header().Set("Content-Type", "image/gif")
		return nil
	case "data":
		w.Header().Set("Content-Type", "text/plain")
		return nil
	case "webp":
		w.Header().Set("Content-Type", "image/webp")
		return nil
	default:
		return ErrInvalidImageExt
	}
}

func RespondBadRequest(w http.ResponseWriter, reason string) {
	w.Header().Set("X-Kinu-BadRequest-Reason", reason)
	w.WriteHeader(http.StatusBadRequest)
}

func RespondNotFound(w http.ResponseWriter) {
	w.WriteHeader(http.StatusNotFound)
}

func RespondInternalServerError(w http.ResponseWriter, err error) {
	if UseSentry {
		raven.CaptureError(err, nil)
	}
	logger.ErrorDebug(err)
	w.WriteHeader(http.StatusInternalServerError)
}

func RespondServiceUnavailable(w http.ResponseWriter, err error) {
	logger.ErrorDebug(err)
	w.WriteHeader(http.StatusServiceUnavailable)
}

type UploadResult struct {
	ImageType string `json:"name"`
	ImageId   string `json:"id"`
}

func RespondImageUploadSuccessJson(w http.ResponseWriter, imageType string, imageId string) {
	json, err := json.Marshal(&UploadResult{ImageType: imageType, ImageId: imageId})
	if err != nil {
		RespondInternalServerError(w, err)
	}
	RespondJson(w, json)
}

func RespondJson(w http.ResponseWriter, json []byte) {
	w.Write(json)
}

func RespondImage(w http.ResponseWriter, bytes []byte) {
	w.Write(bytes)
}

func RespondDataURI(w http.ResponseWriter, bytes []byte) {
	io.WriteString(w, dataurl.EncodeBytes(bytes))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "Host missing\n")
		os.Exit(1)
	}
	domain := os.Args[1]
	daysleft, err := checkssl(domain)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
		os.Exit(2)
	}
	fmt.Println(daysleft)
}

func checkssl(domain string) (int64, error) {
	conn, err := tls.Dial("tcp", domain+":443", nil)
	if err != nil {
		return -1, err
	}
	defer conn.Close()
	now := time.Now().Unix()
	var min int64 = 1000000
	for _, chain := range conn.ConnectionState().VerifiedChains {
		for _, cert := range chain {
			for _, name := range cert.DNSNames {
				if s, e := filepath.Match("/"+name, "/"+domain); s && e == nil {
					left := cert.NotAfter.Unix() - now
					daysleft := (left / 60 / 60 / 24)
					if daysleft < min {
						min = daysleft
					}
				}
			}
		}
	}
	if min >= 1000000 {
		return -2, errors.New("No valid Expiration found\n")
	} else {
		return min, nil
	}
}
<commit_msg>better error messages<commit_after>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "Host missing\n")
		os.Exit(-2)
	}
	domain := os.Args[1]
	daysleft, err := checkssl(domain)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
		fmt.Println(-1)
		os.Exit(-1)
	}
	fmt.Println(daysleft)
}

func checkssl(domain string) (int64, error) {
	conn, err := tls.Dial("tcp", domain+":443", nil)
	if err != nil {
		return -1, err
	}
	defer conn.Close()
	now := time.Now().Unix()
	var min int64 = 1000000
	for _, chain := range conn.ConnectionState().VerifiedChains {
		for _, cert := range chain {
			for _, name := range cert.DNSNames {
				if s, e := filepath.Match("/"+name, "/"+domain); s && e == nil {
					left := cert.NotAfter.Unix() - now
					daysleft := (left / 60 / 60 / 24)
					if daysleft < min {
						min = daysleft
					}
				}
			}
		}
	}
	if min >= 1000000 {
		return -2, errors.New("No valid Expiration found\n")
	} else {
		return min, nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
)

func redirect(w http.ResponseWriter, r *http.Request) {
	log.Printf("Redirecting to %s\n", "https://"+r.Host+r.URL.Path)
	http.Redirect(w, r, "https://"+r.Host+r.URL.Path, http.StatusMovedPermanently)
}

func main() {
	http.HandleFunc("/", redirect)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
	log.Println("Listening on :9090")
}
<commit_msg>Move log line<commit_after>package main

import (
	"log"
	"net/http"
)

func redirect(w http.ResponseWriter, r *http.Request) {
	log.Printf("Redirecting to %s\n", "https://"+r.Host+r.URL.Path)
	http.Redirect(w, r, "https://"+r.Host+r.URL.Path, http.StatusMovedPermanently)
}

func main() {
	http.HandleFunc("/", redirect)
	log.Println("Listening on :9090")
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"github.com/pkg/profile"
	"github.com/verath/archipelago/lib"
	"net/http"
	"os"
	"os/signal"
	"path"
	"syscall"
	"time"
)

const (
	httpReadTimeout  = 10 * time.Second
	httpWriteTimeout = 10 * time.Second
)

func main() {
	var (
		debug       bool
		serveStatic bool
		serverAddr  string
		profileMode string
	)
	flag.BoolVar(&debug, "debug", false, "Set to true to log debug messages.")
	flag.BoolVar(&serveStatic, "servestatic", false, "Enable serving of static assets.")
	flag.StringVar(&serverAddr, "addr", ":8080", "TCP address for the http server to listen on.")
	flag.StringVar(&profileMode, "profile", "", "Enable profiling mode, one of [cpu, mem, mutex, block]")
	flag.Parse()

	logger := logrus.New()
	logger.Formatter = &logrus.TextFormatter{}
	if debug {
		logger.Level = logrus.DebugLevel
	}
	// Setup profiling, if profile flag was set
	switch profileMode {
	case "cpu":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop()
	case "mem":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MemProfile).Stop()
	case "mutex":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MutexProfile).Stop()
	case "block":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.BlockProfile).Stop()
	default:
	}

	archipelagoServer, err := archipelago.New(logger)
	if err != nil {
		logger.Fatalf("Error creating game: %+v", err)
	}

	http.Handle("/ws", archipelagoServer.WebsocketHandler())
	http.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})
	if serveStatic {
		staticPath := "./web/dist"
		// Explicitly set a max-age of 0 for service worker script.
		http.HandleFunc("/sw.js", func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Cache-Control", "max-age=0")
			http.ServeFile(w, r, path.Join(staticPath, "sw.js"))
		})
		http.Handle("/", http.FileServer(http.Dir(staticPath)))
	}
	httpServer := &http.Server{
		Addr:         serverAddr,
		ReadTimeout:  httpReadTimeout,
		WriteTimeout: httpWriteTimeout,
	}

	ctx, cancel := context.WithCancel(lifetimeContext(logger))
	errCh := make(chan error)
	go func() { errCh <- archipelagoServer.Run(ctx) }()
	go func() { errCh <- runHTTPServer(ctx, httpServer) }()
	err = <-errCh
	cancel()
	<-errCh
	if errors.Cause(err) == context.Canceled {
		logger.Debugf("Error caught in main: %+v", err)
	} else {
		logger.Fatalf("Error caught in main: %+v", err)
	}
}

// lifetimeContext returns a context that is cancelled on the first SIGINT, SIGTERM,
// or SIGKILL signal received. The application is force closed if more than one
// signal is received.
func lifetimeContext(logger *logrus.Logger) context.Context {
	ctx, cancel := context.WithCancel(context.Background())
	stopSigs := []os.Signal{os.Interrupt, os.Kill, syscall.SIGTERM}
	stopCh := make(chan os.Signal, len(stopSigs))
	signal.Notify(stopCh, stopSigs...)
	go func() {
		<-stopCh
		logger.Info("Caught interrupt, shutting down")
		cancel()
		<-stopCh
		logger.Fatal("Caught second interrupt, force closing")
	}()
	return ctx
}

// runHTTPServer starts and runs the given HTTP server until either an error
// occurs or the context is cancelled.
func runHTTPServer(ctx context.Context, server *http.Server) error {
	errCh := make(chan error)
	go func() { errCh <- server.ListenAndServe() }()
	select {
	case err := <-errCh:
		return err
	case <-ctx.Done():
		server.Close()
		return ctx.Err()
	}
}
<commit_msg>Adds log for service started and stopped.<commit_after>package main

import (
	"context"
	"flag"
	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"github.com/pkg/profile"
	"github.com/verath/archipelago/lib"
	"net/http"
	"os"
	"os/signal"
	"path"
	"syscall"
	"time"
)

const (
	httpReadTimeout  = 10 * time.Second
	httpWriteTimeout = 10 * time.Second
)

func main() {
	var (
		debug       bool
		serveStatic bool
		serverAddr  string
		profileMode string
	)
	flag.BoolVar(&debug, "debug", false, "Set to true to log debug messages.")
	flag.BoolVar(&serveStatic, "servestatic", false, "Enable serving of static assets.")
	flag.StringVar(&serverAddr, "addr", ":8080", "TCP address for the http server to listen on.")
	flag.StringVar(&profileMode, "profile", "", "Enable profiling mode, one of [cpu, mem, mutex, block]")
	flag.Parse()

	logger := logrus.New()
	logger.Formatter = &logrus.TextFormatter{}
	if debug {
		logger.Level = logrus.DebugLevel
	}
	// Setup profiling, if profile flag was set
	switch profileMode {
	case "cpu":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop()
	case "mem":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MemProfile).Stop()
	case "mutex":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.MutexProfile).Stop()
	case "block":
		defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.BlockProfile).Stop()
	default:
	}

	archipelagoServer, err := archipelago.New(logger)
	if err != nil {
		logger.Fatalf("Error creating game: %+v", err)
	}

	http.Handle("/ws", archipelagoServer.WebsocketHandler())
	http.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})
	if serveStatic {
		staticPath := "./web/dist"
		// Explicitly set a max-age of 0 for service worker script.
		http.HandleFunc("/sw.js", func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Cache-Control", "max-age=0")
			http.ServeFile(w, r, path.Join(staticPath, "sw.js"))
		})
		http.Handle("/", http.FileServer(http.Dir(staticPath)))
	}
	httpServer := &http.Server{
		Addr:         serverAddr,
		ReadTimeout:  httpReadTimeout,
		WriteTimeout: httpWriteTimeout,
	}

	ctx, cancel := context.WithCancel(lifetimeContext(logger))
	errCh := make(chan error)
	go func() { errCh <- archipelagoServer.Run(ctx) }()
	go func() { errCh <- runHTTPServer(ctx, httpServer) }()
	logger.Infof("Archipelago backend started: '%s'", serverAddr)
	err = <-errCh
	cancel()
	<-errCh
	if errors.Cause(err) == context.Canceled {
		logger.Debugf("Error caught in main: %+v", err)
	} else {
		logger.Fatalf("Error caught in main: %+v", err)
	}
	logger.Info("Archipelago backend stopped.")
}

// lifetimeContext returns a context that is cancelled on the first SIGINT, SIGTERM,
// or SIGKILL signal received. The application is force closed if more than one
// signal is received.
func lifetimeContext(logger *logrus.Logger) context.Context {
	ctx, cancel := context.WithCancel(context.Background())
	stopSigs := []os.Signal{os.Interrupt, os.Kill, syscall.SIGTERM}
	stopCh := make(chan os.Signal, len(stopSigs))
	signal.Notify(stopCh, stopSigs...)
	go func() {
		<-stopCh
		logger.Info("Caught interrupt, shutting down")
		cancel()
		<-stopCh
		logger.Fatal("Caught second interrupt, force closing")
	}()
	return ctx
}

// runHTTPServer starts and runs the given HTTP server until either an error
// occurs or the context is cancelled.
func runHTTPServer(ctx context.Context, server *http.Server) error {
	errCh := make(chan error)
	go func() { errCh <- server.ListenAndServe() }()
	select {
	case err := <-errCh:
		return err
	case <-ctx.Done():
		server.Close()
		return ctx.Err()
	}
}
<|endoftext|>


<commit_before>// 4 march 2014
package main

import (
	"fmt"
	"os"
//	"os/exec"
	"time"
	"github.com/andlabs/ui"
)

const (
	defCmdLine = "mpv -loop inf ~/ring.wav"
	defTime = "10:00 AM"
	timeFmt = "3:04 PM"
)

func myMain() {
	w := ui.NewWindow("wakeup", 400, 100)
	w.Closing = ui.Event()
	cmdbox := ui.NewLineEdit(defCmdLine)
	timebox := ui.NewLineEdit(defTime)
	bStart := ui.NewButton("Start")
	bStop := ui.NewButton("Stop")

	// a Stack to keep both buttons at the same size
	btnbox := ui.NewStack(ui.Horizontal, bStart, bStop)
	btnbox.SetStretchy(0)
	btnbox.SetStretchy(1)
	// and a Stack around that Stack to keep them at a reasonable size
	btnbox = ui.NewStack(ui.Horizontal, btnbox)

	// the main layout
	grid := ui.NewGrid(2,
		ui.NewLabel("Command"), cmdbox,
		ui.NewLabel("Time"), timebox,
		ui.Space(), ui.Space(),		// the Space on the right will consume the window blank space
		ui.Space(), btnbox)
	grid.SetStretchy(2, 1)			// make the Space noted above consume
	grid.SetFilling(0, 1)				// make the two textboxes grow horizontally
	grid.SetFilling(1, 1)

	err := w.Open(grid)
	if err != nil {
		ui.MsgBoxError("wakeup", "Error opening window: %v", err)
		os.Exit(1)
	}

mainloop:
	for {
		select {
		case <-w.Closing:
			break mainloop
		case <-bStart.Clicked:
			alarmTime, err := time.Parse(timeFmt, timebox.Text())
			if err != nil {
				ui.MsgBoxError("wakeup",
					"Error parsing time %q: %v\nMake sure your time is in the form %q (without quotes.",
					timebox.Text(), err, timeFmt)
				continue
			}
			fmt.Println(alarmTime, time.Now().Sub(alarmTime))
			// TODO
		case <-bStop.Clicked:
			// TODO
		}
	}
}

func main() {
	err := ui.Go(myMain)
	if err != nil {
		panic(fmt.Errorf("error initializing UI library: %v", err))
	}
}
<commit_msg>Added an appropriate ETA calculation.<commit_after>// 4 march 2014
package main

import (
	"fmt"
	"os"
//	"os/exec"
	"time"
	"github.com/andlabs/ui"
)

const (
	defCmdLine = "mpv -loop inf ~/ring.wav"
	defTime = "10:00 AM"
	timeFmt = "3:04 PM"
)

// If later hasn't happened yet, make it happen on the day of now; if not, the day after.
func bestTime(now time.Time, later time.Time) time.Time {
	now = now.Local()		// use local time to make things make sense
	nowh, nowm, nows := now.Clock()
	laterh, laterm, laters := later.Clock()
	add := false
	if nowh > laterh {
		add = true
	} else if (nowh == laterh) && (nowm > laterm) {
		add = true
	} else if (nowh == laterh) && (nowm == laterm) && (nows >= laters) {
		// >= in the case we're on the exact second; add a day because the alarm should have gone off by now otherwise!
		add = true
	}
println(nowh,nowm,nows,add,laterh,laterm,laters)
	if add {
		now = now.AddDate(0, 0, 1)
	}
	return time.Date(now.Year(), now.Month(), now.Day(),
		laterh, laterm, laters, 0,
		now.Location())
}

func myMain() {
	w := ui.NewWindow("wakeup", 400, 100)
	w.Closing = ui.Event()
	cmdbox := ui.NewLineEdit(defCmdLine)
	timebox := ui.NewLineEdit(defTime)
	bStart := ui.NewButton("Start")
	bStop := ui.NewButton("Stop")

	// a Stack to keep both buttons at the same size
	btnbox := ui.NewStack(ui.Horizontal, bStart, bStop)
	btnbox.SetStretchy(0)
	btnbox.SetStretchy(1)
	// and a Stack around that Stack to keep them at a reasonable size
	btnbox = ui.NewStack(ui.Horizontal, btnbox)

	// the main layout
	grid := ui.NewGrid(2,
		ui.NewLabel("Command"), cmdbox,
		ui.NewLabel("Time"), timebox,
		ui.Space(), ui.Space(),		// the Space on the right will consume the window blank space
		ui.Space(), btnbox)
	grid.SetStretchy(2, 1)			// make the Space noted above consume
	grid.SetFilling(0, 1)				// make the two textboxes grow horizontally
	grid.SetFilling(1, 1)

	err := w.Open(grid)
	if err != nil {
		ui.MsgBoxError("wakeup", "Error opening window: %v", err)
		os.Exit(1)
	}

mainloop:
	for {
		select {
		case <-w.Closing:
			break mainloop
		case <-bStart.Clicked:
			alarmTime, err := time.Parse(timeFmt, timebox.Text())
			if err != nil {
				ui.MsgBoxError("wakeup",
					"Error parsing time %q: %v\nMake sure your time is in the form %q (without quotes.",
					timebox.Text(), err, timeFmt)
				continue
			}
			now := time.Now()
			later := bestTime(now, alarmTime)
			fmt.Println(later, later.Sub(now))
			// TODO
		case <-bStop.Clicked:
			// TODO
		}
	}
}

func main() {
	err := ui.Go(myMain)
	if err != nil {
		panic(fmt.Errorf("error initializing UI library: %v", err))
	}
}
<|endoftext|>


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

package main

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

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
			    if strings.Contains(message.Text, "我是誰") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("您是昭宗大哥嗎？")).Do();
				} else if strings.Contains(message.Text, "不對") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("您是信宏兄嗎？")).Do();
				} else if strings.Contains(message.Text, "不是") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("您是家興嗎？")).Do();
				} else if strings.Contains(message.Text, "再猜") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("我知道了，您是志鴻！")).Do();
				} else if strings.Contains(message.Text, "答對") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("需要我做些什麼嗎？")).Do();
				} else if strings.Contains(message.Text, "HIGGS.csv") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("HIGGS.csv 檔案超大，要跑很久喔！")).Do();
				} else if strings.Contains(message.Text, "算了") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("再下一個命令？")).Do();
				} else if strings.Contains(message.Text, "pca") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewImageMessage("https://github.com/jihghong/LineBotInstance/images/pca.PNG", "https://github.com/jihghong/LineBotInstance/images/pca.PNG")).Do();
				} else {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("歡迎下次再來")).Do();
				}

			case *linebot.ImageMessage:
				bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("哈哈哈！有創意")).Do();
			｝
		}
	}
}
<commit_msg>change image URL to raw.githubusercontent<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

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

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
			    if strings.Contains(message.Text, "我是誰") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("您是昭宗大哥嗎？")).Do();
				} else if strings.Contains(message.Text, "不對") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("您是信宏兄嗎？")).Do();
				} else if strings.Contains(message.Text, "不是") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("您是家興嗎？")).Do();
				} else if strings.Contains(message.Text, "再猜") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("我知道了，您是志鴻！")).Do();
				} else if strings.Contains(message.Text, "答對") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("需要我做些什麼嗎？")).Do();
				} else if strings.Contains(message.Text, "HIGGS.csv") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("HIGGS.csv 檔案超大，要跑很久喔！")).Do();
				} else if strings.Contains(message.Text, "算了") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("再下一個命令？")).Do();
				} else if strings.Contains(message.Text, "pca") {
					bot.ReplyMessage(event.ReplyToken, linebot.NewImageMessage("https://raw.githubusercontent.com/jihghong/LineBotInstance/master/images/pca.PNG", "https://raw.githubusercontent.com/jihghong/LineBotInstance/master/images/pca.PNG")).Do();
				} else {
					bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("歡迎下次再來")).Do();
				}

			case *linebot.ImageMessage:
				bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("哈哈哈！有創意")).Do();
			｝
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"time"
)

var replace = map[string]func(){
	"nnf":       nnf,
	"nnl":       nnl,
	"dbg":       dbg,
	"openFile":  openFile,
	"readFile":  readFile,
	"getURL":    getURL,
	"reqStdin":  reqStdin,
	"goMain":    goMain,
	"tempFile":  tempFile,
	"serveHTTP": serveHTTP,
	"pymain":    pyMain,
	"html5":     html5,
	"now":       now,
	"ubb":       bash,
	"ubp":       python,
	"gomain":    goMain,
	"flagsh":    flagsh,
	"dummyType": dummyType,
	"jm":        jsonMarshal,
	"ju":        jsonUnmarshal,
	"rb":        readBody,
	"watcher":   watcher,
}

var update = map[string]func(string){
	"fpl(":  fpl,
	"lpf(":  lpf,
	"lpl(":  lpl,
	"fpf(":  fpf,
	"hfunc": hfunc,
	"ow:":   pyOpenWrite,
	"ul":    ul,
}

var modify = map[string]func(string){
	"_ctx,": contextArg}

func main() {
	if os.Getenv("shortcuts") == "off" {
		return
	}

	stat, err := os.Stdin.Stat()
	if err != nil {
		log.Fatal(err)
	}
	if stat.Mode()&os.ModeCharDevice != 0 {
		log.Fatal("please pipe in some data")
	}

	s := bufio.NewScanner(os.Stdin)
	var lastBlank bool

	for s.Scan() {
		line := s.Text()
		trim := strings.TrimSpace(line)

		if f, found := replace[trim]; found {
			f()
			continue
		}

		var replaced bool
		for pre, f := range update {
			if strings.HasPrefix(trim, pre) {
				f(line)
				replaced = true
				break
			}

		}

		for pre, f := range modify {
			if strings.Contains(line, pre) {
				f(line)
				replaced = true
				break
			}
		}

		if replaced {
			continue
		}

		if strings.TrimSpace(line) == "" {
			if lastBlank {
				continue
			}
			lastBlank = true
		} else {
			lastBlank = false
		}
		fmt.Println(line)
	}
}

func nnf() {
	fmt.Println(`if err != nil{
	 log.Fatalf("Failed to do something: %s\n", err)
	 }`)
}

func nnl() {
	fmt.Println(`if err != nil{
	 log.Printf("Failed to do something: %s\n", err)
	 }`)
}

func dbg() {
	weekday := string([]rune(time.Now().Weekday().String())[0])
	day := time.Now().Day()
	fmt.Printf(`log.Printf("%s%d %%v", x)\n`, weekday, day)
}

func lpf(line string) {
	fmt.Println(strings.Replace(line, "lpf(", "log.Printf(", 1))
}

func fpf(line string) {
	fmt.Println(strings.Replace(line, "fpf(", "fmt.Printf(", 1))
}

func lpl(line string) {
	fmt.Println(strings.Replace(line, "lpl(", "log.Println(", 1))
}

func fpl(line string) {
	fmt.Println(strings.Replace(line, "fpl(", "fmt.Println(", 1))
}

func goMain() {
	fmt.Println(`package main

import (
    "fmt"
)

func main() {
    fmt.Println("gopher")
}
`)
}

func serveHTTP() {
	fmt.Println(`package main

import (
    "fmt"
	 "net/http"
)

func main() {
    http.HandleFunc("/", index)
	 http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request){
	 fmt.Fprintf(w, "hello")
}
`)
}

func pyMain() {
	fmt.Println(`#!/usr/bin/env python3
"""
You should probably write something here.
"""

def main():
    """
    Do the thing.
    """
    print("python")

if __name__ == '__main__':
    main()
`)
}

func html5() {
	fmt.Println(`<!DOCTYPE html>

		 <meta charset="UTF-8">
		 <title>title</title>
        <link rel="stylesheet" href="https://aoeus.com/milligram.min.css" type="text/css">
		 <meta name="viewport" content="width-device-width, initial-scale=1">

topic

content

`)
}

func hfunc(line string) {
	parts := strings.Split(line, " ")
	name := "index"
	if len(parts) > 1 {
		name = parts[1]
	}
	fmt.Printf(`func %s(w http.ResponseWriter, r *http.Request){
	 }`, name)
}

func pyOpenWrite(line string) {
	l := len(line) - len(strings.TrimLeft(line, " "))
	pad := strings.Repeat(" ", l)
	lines := []string{
		`with open("out.txt", "wb") as raw:`,
		`    raw.write("{0}\n".format(msg))`,
	}

	for _, line = range lines {
		fmt.Printf("%s%s\n", pad, line)
	}

}

func contextArg(line string) {
	line = strings.Replace(line, "_ctx,", "ctx context.Context,", 1)
	fmt.Printf("%s\n", line)
}

func ul(line string) {
	trim := strings.TrimSpace(line)
	margin := len(line) - len(strings.TrimLeft(trim, " \t"))
	padding := strings.Repeat(" ", margin)
	fmt.Printf(padding)
	fmt.Println("
")
	for i := 0; i < 3; i++ {
		fmt.Printf(padding)
		fmt.Println("\t
")
		fmt.Printf(padding)
		fmt.Println("\t\tthing")
		fmt.Printf(padding)
		fmt.Println("\t
")
	}
	fmt.Printf(padding)
	fmt.Println("
")

}

func openFile() {
	fmt.Println(`f, err := os.Open(filename)
	 if err != nil {
		 log.Printf("Unable to open %q: %s\n", filename, err)
	 }
	 defer f.Close()`)
}

func readFile() {
	fmt.Println(`b, err := ioutil.ReadFile(filename)
	 if err != nil {
		 log.Printf("Unable to open %q: %s\n", filename, err)
	 }`)
}

func getURL() {
	fmt.Println(`resp, err := http.Get(link)
	 if err != nil {
		 log.Printf("Unable to fetch %q: %s\n", link, err)
		 return
	 }
	 defer resp.Body.Close()
	 b, err := ioutil.ReadAll(resp.Body)
	 if err != nil {
		 log.Printf("Unable to read response from  %q: %s\n", link, err)
		 return
	 }
	 `)
}

func reqStdin() {
	fmt.Println(`stat, err := os.Stdin.Stat()
	 if err != nil {
		 log.Fatal(err)
	 }
	 if stat.Mode()&os.ModeCharDevice != 0 {
		 log.Fatal("please pipe in some data")
	 }`)
}

func tempFile() {
	fmt.Println(`t, err := ioutil.TempFile("", "temp")
if err != nil{
	 log.Fatalf("Unable to create temp file: %s\n", err)
}
fmt.Printf("Created temp file %q\n", t.Name())
defer t.Close()
`)
}

func now() {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
}

func bash() {
	fmt.Println("#!/usr/bin/env bash")
}

func python() {
	fmt.Println("#!/usr/bin/env python")
}

func flagsh() {

	fmt.Println(`#!/usr/bin/env bash

flag=$(mktemp)
touch $flag

while true; do
sleep 5
    find . -mmin -1 -name '*.go' 2>>/dev/null | while read file; do
        if [[ "$file" -nt $flag ]]; then
            if [[ "$file" == "$flag" ]]; then
                continue
            fi
            echo "$file was updated"
            touch $flag
        fi
    done
done
`)
}

func dummyType() {
	fmt.Println(`type dummy struct {
    thing string
    size int
    color string
}
`)
}
func jsonMarshal() {
	fmt.Println(`b, err := json.Marshal(x)
    if err != nil{
        log.Fatalf("Failed to do something: %s\n", err)
	 } `)
}

func jsonUnmarshal() {
	fmt.Println(`var x thing
err = json.Unmarshal(b, &x)
return x, err
`)
}

func readBody() {
	fmt.Println(`b, err := ioutil.ReadAll(r.Body)
	 if err != nil {
		 return err
	 }
	 defer r.Body.Close()`)
}

func watcher() {
	fmt.Println(`#!/usr/bin/env bash

bin=monkey
flag=$(mktemp)
code=main.go

function cleanup() {
    pkill $bin
    rm -f $bin
    rm -f $flag
    exit
}

trap cleanup SIGINT SIGTERM

while true
do
    if [ ! "$code" -nt $flag ]; then
        sleep 5
        continue
    fi
    go build -o $bin $code || { touch $flag; continue; }
    pkill $bin
    ./$bin &
    sleep 1
    ./tests.sh
    touch $flag
    sleep 15
done`)
}
<commit_msg>add audit, lg, and exempt<commit_after>package main

// #exempt

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"time"
)

var replace = map[string]func(){
	"_audit":    audit,
	"_lg":       lg,
	"nnf":       nnf,
	"nnl":       nnl,
	"dbg":       dbg,
	"openFile":  openFile,
	"readFile":  readFile,
	"getURL":    getURL,
	"reqStdin":  reqStdin,
	"goMain":    goMain,
	"tempFile":  tempFile,
	"serveHTTP": serveHTTP,
	"pymain":    pyMain,
	"html5":     html5,
	"now":       now,
	"ubb":       bash,
	"ubp":       python,
	"gomain":    goMain,
	"flagsh":    flagsh,
	"dummyType": dummyType,
	"jm":        jsonMarshal,
	"ju":        jsonUnmarshal,
	"rb":        readBody,
	"watcher":   watcher,
}

var update = map[string]func(string){
	"fpl(":  fpl,
	"lpf(":  lpf,
	"lpl(":  lpl,
	"fpf(":  fpf,
	"hfunc": hfunc,
	"ow:":   pyOpenWrite,
	"ul":    ul,
}

var modify = map[string]func(string){
	"ctx context.Context,": contextArg}

func main() {
	stat, err := os.Stdin.Stat()
	if err != nil {
		log.Fatal(err)
	}
	if stat.Mode()&os.ModeCharDevice != 0 {
		log.Fatal("please pipe in some data")
	}

	s := bufio.NewScanner(os.Stdin)
	var lastBlank bool

	var exempt bool

	for s.Scan() {
		line := s.Text()
		if strings.TrimSpace(line) == "// #exempt" {
			exempt = true
		}

		if exempt {
			fmt.Println(strings.TrimSuffix(line, "\n"))
			continue
		}

		trim := strings.TrimSpace(line)

		if f, found := replace[trim]; found {
			f()
			continue
		}

		var replaced bool
		for pre, f := range update {
			if strings.HasPrefix(trim, pre) {
				f(line)
				replaced = true
				break
			}

		}

		for pre, f := range modify {
			if strings.Contains(line, pre) {
				f(line)
				replaced = true
				break
			}
		}

		if replaced {
			continue
		}

		if strings.TrimSpace(line) == "" {
			if lastBlank {
				continue
			}
			lastBlank = true
		} else {
			lastBlank = false
		}
		fmt.Println(line)
	}
}

func audit() {
	fmt.Printf(`// Last audit %s by skm.`, time.Now().Format("2006-01-02"))
}

func lg() {
	fmt.Println(`lg := logger.New(ctx)`)
}

func nnf() {
	fmt.Println(`if err != nil{
	 log.Fatalf("Failed to do something: %s\n", err)
	 }`)
}

func nnl() {
	fmt.Println(`if err != nil{
	 log.Printf("Failed to do something: %s\n", err)
	 }`)
}

func dbg() {
	weekday := string([]rune(time.Now().Weekday().String())[0])
	day := time.Now().Day()
	fmt.Printf(`log.Printf("%s%d %%v", x)\n`, weekday, day)
}

func lpf(line string) {
	fmt.Println(strings.Replace(line, "lpf(", "log.Printf(", 1))
}

func fpf(line string) {
	fmt.Println(strings.Replace(line, "fpf(", "fmt.Printf(", 1))
}

func lpl(line string) {
	fmt.Println(strings.Replace(line, "lpl(", "log.Println(", 1))
}

func fpl(line string) {
	fmt.Println(strings.Replace(line, "fpl(", "fmt.Println(", 1))
}

func goMain() {
	fmt.Println(`package main

import (
    "fmt"
)

func main() {
    fmt.Println("gopher")
}
`)
}

func serveHTTP() {
	fmt.Println(`package main

import (
    "fmt"
	 "net/http"
)

func main() {
    http.HandleFunc("/", index)
	 http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request){
	 fmt.Fprintf(w, "hello")
}
`)
}

func pyMain() {
	fmt.Println(`#!/usr/bin/env python3
"""
You should probably write something here.
"""

def main():
    """
    Do the thing.
    """
    print("python")

if __name__ == '__main__':
    main()
`)
}

func html5() {
	fmt.Println(`<!DOCTYPE html>

		 <meta charset="UTF-8">
		 <title>title</title>
        <link rel="stylesheet" href="https://aoeus.com/milligram.min.css" type="text/css">
		 <meta name="viewport" content="width-device-width, initial-scale=1">

topic

content

`)
}

func hfunc(line string) {
	parts := strings.Split(line, " ")
	name := "index"
	if len(parts) > 1 {
		name = parts[1]
	}
	fmt.Printf(`func %s(w http.ResponseWriter, r *http.Request){
	 }`, name)
}

func pyOpenWrite(line string) {
	l := len(line) - len(strings.TrimLeft(line, " "))
	pad := strings.Repeat(" ", l)
	lines := []string{
		`with open("out.txt", "wb") as raw:`,
		`    raw.write("{0}\n".format(msg))`,
	}

	for _, line = range lines {
		fmt.Printf("%s%s\n", pad, line)
	}

}

func contextArg(line string) {
	line = strings.Replace(line, "_ctx,", "ctx context.Context,", 1)
	fmt.Printf("%s\n", line)
}

func ul(line string) {
	trim := strings.TrimSpace(line)
	margin := len(line) - len(strings.TrimLeft(trim, " \t"))
	padding := strings.Repeat(" ", margin)
	fmt.Printf(padding)
	fmt.Println("
")
	for i := 0; i < 3; i++ {
		fmt.Printf(padding)
		fmt.Println("\t
")
		fmt.Printf(padding)
		fmt.Println("\t\tthing")
		fmt.Printf(padding)
		fmt.Println("\t
")
	}
	fmt.Printf(padding)
	fmt.Println("
")

}

func openFile() {
	fmt.Println(`f, err := os.Open(filename)
	 if err != nil {
		 log.Printf("Unable to open %q: %s\n", filename, err)
	 }
	 defer f.Close()`)
}

func readFile() {
	fmt.Println(`b, err := ioutil.ReadFile(filename)
	 if err != nil {
		 log.Printf("Unable to open %q: %s\n", filename, err)
	 }`)
}

func getURL() {
	fmt.Println(`resp, err := http.Get(link)
	 if err != nil {
		 log.Printf("Unable to fetch %q: %s\n", link, err)
		 return
	 }
	 defer resp.Body.Close()
	 b, err := ioutil.ReadAll(resp.Body)
	 if err != nil {
		 log.Printf("Unable to read response from  %q: %s\n", link, err)
		 return
	 }
	 `)
}

func reqStdin() {
	fmt.Println(`stat, err := os.Stdin.Stat()
	 if err != nil {
		 log.Fatal(err)
	 }
	 if stat.Mode()&os.ModeCharDevice != 0 {
		 log.Fatal("please pipe in some data")
	 }`)
}

func tempFile() {
	fmt.Println(`t, err := ioutil.TempFile("", "temp")
if err != nil{
	 log.Fatalf("Unable to create temp file: %s\n", err)
}
fmt.Printf("Created temp file %q\n", t.Name())
defer t.Close()
`)
}

func now() {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
}

func bash() {
	fmt.Println("#!/usr/bin/env bash")
}

func python() {
	fmt.Println("#!/usr/bin/env python")
}

func flagsh() {

	fmt.Println(`#!/usr/bin/env bash

flag=$(mktemp)
touch $flag

while true; do
sleep 5
    find . -mmin -1 -name '*.go' 2>>/dev/null | while read file; do
        if [[ "$file" -nt $flag ]]; then
            if [[ "$file" == "$flag" ]]; then
                continue
            fi
            echo "$file was updated"
            touch $flag
        fi
    done
done
`)
}

func dummyType() {
	fmt.Println(`type dummy struct {
    thing string
    size int
    color string
}
`)
}
func jsonMarshal() {
	fmt.Println(`b, err := json.Marshal(x)
    if err != nil{
        log.Fatalf("Failed to do something: %s\n", err)
	 } `)
}

func jsonUnmarshal() {
	fmt.Println(`var x thing
err = json.Unmarshal(b, &x)
return x, err
`)
}

func readBody() {
	fmt.Println(`b, err := ioutil.ReadAll(r.Body)
	 if err != nil {
		 return err
	 }
	 defer r.Body.Close()`)
}

func watcher() {
	fmt.Println(`#!/usr/bin/env bash

bin=monkey
flag=$(mktemp)
code=main.go

function cleanup() {
    pkill $bin
    rm -f $bin
    rm -f $flag
    exit
}

trap cleanup SIGINT SIGTERM

while true
do
    if [ ! "$code" -nt $flag ]; then
        sleep 5
        continue
    fi
    go build -o $bin $code || { touch $flag; continue; }
    pkill $bin
    ./$bin &
    sleep 1
    ./tests.sh
    touch $flag
    sleep 15
done`)
}
<|endoftext|>


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

 package dlshared

import (
	"sync"
	"time"
)

// The consumer is a generic component that is meant to be embedded into your applications. It
// allows you to create a specific number of goroutines to handle the realtime data/msg processing.
// It also has optional support for spillover processing if the goroutines cannot keep pace with
// the incoming events. If your spillover method blocks (i.e., cannot keep pace), it will cause further
// issues with incoming message processing. If you do not provide a spillover method, data will be dropped
// and a log statement will be generated for each discarded message.
// The contact is that you must call the NewConsumer method to create the struct.
// After that, you must call the Start/Stop methods before attempting to pass in a msg. If the passed
// consume function panics, the consumer will lose a goroutine each time, so don't panic!
type Consumer struct {

	logger Logger

	name string

	quitChannel chan bool

	consumeFunc func(msg interface{})

	spilloverFunc func(msg interface{})

	processorChannel chan interface{}

	receiveChannel chan interface{}

	processedChannel chan bool

	maxGoroutines int

	maxWaitOnStopInMs int64

	waitGroup *sync.WaitGroup
}

// Create the consumer. You must call this method to create a consumer. If you set the maxGoroutines
// to zero (or less) then it will spawn a new goroutine for each request and there is no limit. If maxGoroutines
// is set, these goroutines are created and added to a pool when Start is called. If you set channelBufferSize to something
// greater than zero then a buffered channel is created. The maxWaitOnStopMs is the amount of time the Stop
// method will wait for the goroutines to finish clearing out the processor channel. If the processor channel unbuffered,
// this does not apply. A maxWaitOnStopMs value of zero indicates an unlimited wait.
func NewConsumer(	name string,
					receiveChannel chan interface{},
					consumeFunc func(msg interface{}),
					spilloverFunc func(msg interface{}),
					maxGoroutines, channelBufferSize, maxWaitOnStopInMs int,
					logger Logger) *Consumer {

	if channelBufferSize < 0 { channelBufferSize = 0 }

	if maxWaitOnStopInMs < 0 { maxWaitOnStopInMs = 0 }

	if maxGoroutines <= 0 { maxGoroutines = 1 }

	consumer := &Consumer{
		logger: logger,
		name: name,
		consumeFunc: consumeFunc,
		spilloverFunc: spilloverFunc,
		receiveChannel: receiveChannel,
		processorChannel: make(chan interface{}, channelBufferSize),
		quitChannel: make(chan bool),
		processedChannel: make(chan bool, maxGoroutines),
		maxGoroutines: maxGoroutines,
		maxWaitOnStopInMs: int64(maxWaitOnStopInMs),
		waitGroup: new(sync.WaitGroup),
	}

	return consumer
}

// This method listens to the processor channel and then
// calls the consume function passed. The passed consume
// function should never panic. Don't Panic!
func (self *Consumer) msgProcessor() {
	defer self.waitGroup.Done()
	for msg := range self.processorChannel {
		self.consumeFunc(msg)
		self.processedChannel <- true
	}
}

func (self *Consumer) listenForMsgs() {
	defer self.waitGroup.Done()
	msgsBeingProcessed := 0

    for {
        select {
			case msg := <- self.receiveChannel:
				if msgsBeingProcessed <= self.maxGoroutines {

					self.processorChannel <- msg
					msgsBeingProcessed++

				} else {
					if self.spilloverFunc == nil {
						self.logger.Logf(Warn, "Max concurrent messages (%d) reached in consumer: %s - dropping data (no spillover func set)", self.maxGoroutines, self.name)
					} else {
						// If this blocks, it will further compound problems with the consumer
						self.spilloverFunc(msg)
					}
				}
			case <- self.processedChannel: msgsBeingProcessed--
			case <- self.quitChannel: return
        }
    }
}

func (self *Consumer) Start() error {

	// Create the goroutine pool
	for idx := 0; idx < self.maxGoroutines; idx++ {
		self.waitGroup.Add(1)
		go self.msgProcessor()
	}

	// Create the message listener
	self.waitGroup.Add(1)
	go self.listenForMsgs()

	return nil
}

// This method will block until all goroutines exit. It is up to the
// caller to clear the receiveChannel.
func (self *Consumer) Stop() error {

	// Exit the listen for events select
	self.quitChannel <- true

	// If this is an unbuffered channel, close, wait for the goroutines
	// to exit and then get out of here.
	if cap(self.processorChannel) == 0 {
		close(self.processorChannel)
		self.waitGroup.Wait()
		return nil
	}

	// This is a buffered channel so we need to wait until the channel
	// is emptied (if configured to do so).
	if self.maxWaitOnStopInMs > 0 {
		start := CurrentTimeInMillis()

		for {
			if len(self.processorChannel) == 0 {
				break
			}

		 	if CurrentTimeInMillis() - start >= self.maxWaitOnStopInMs {
				break
			}

			time.Sleep(10 * time.Millisecond)
		}
	}

	// Close the processor channel and wait for all of the goroutines to return
	close(self.processorChannel)
	self.waitGroup.Wait()

	return nil
}

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

package dlshared

import (
	"sync"
	"time"
)

// The consumer is a generic component that is meant to be embedded into your applications. It
// allows you to create a specific number of goroutines to handle the realtime data/msg processing.
// It also has optional support for spillover processing if the goroutines cannot keep pace with
// the incoming events. If your spillover method blocks (i.e., cannot keep pace), it will cause further
// issues with incoming message processing. If you do not provide a spillover method, data will be dropped
// and a log statement will be generated for each discarded message.
// The contact is that you must call the NewConsumer method to create the struct.
// After that, you must call the Start/Stop methods before attempting to pass in a msg. If the passed
// consume function panics, the consumer will lose a goroutine each time, so don't panic!
type Consumer struct {

	logger Logger

	name string

	quitChannel chan bool

	consumeFunc func(msg interface{})

	spilloverFunc func(msg interface{})

	processorChannel chan interface{}

	receiveChannel chan interface{}

	processedChannel chan bool

	maxGoroutines int

	maxWaitOnStopInMs int64

	waitGroup *sync.WaitGroup
}

// Create the consumer. You must call this method to create a consumer. If you set the maxGoroutines
// to zero (or less) then it will spawn a new goroutine for each request and there is no limit. If maxGoroutines
// is set, these goroutines are created and added to a pool when Start is called. If you set channelBufferSize to something
// greater than zero then a buffered channel is created. The maxWaitOnStopMs is the amount of time the Stop
// method will wait for the goroutines to finish clearing out the processor channel. If the processor channel unbuffered,
// this does not apply. A maxWaitOnStopMs value of zero indicates an unlimited wait.
func NewConsumer(	name string,
					receiveChannel chan interface{},
					consumeFunc func(msg interface{}),
					spilloverFunc func(msg interface{}),
					maxGoroutines, channelBufferSize, maxWaitOnStopInMs int,
					logger Logger) *Consumer {

	if channelBufferSize < 0 { channelBufferSize = 0 }

	if maxWaitOnStopInMs < 0 { maxWaitOnStopInMs = 0 }

	if maxGoroutines <= 0 { maxGoroutines = 1 }

	consumer := &Consumer{
		logger: logger,
		name: name,
		consumeFunc: consumeFunc,
		spilloverFunc: spilloverFunc,
		receiveChannel: receiveChannel,
		processorChannel: make(chan interface{}, channelBufferSize),
		quitChannel: make(chan bool),
		processedChannel: make(chan bool, maxGoroutines),
		maxGoroutines: maxGoroutines,
		maxWaitOnStopInMs: int64(maxWaitOnStopInMs),
		waitGroup: new(sync.WaitGroup),
	}

	return consumer
}

// This method listens to the processor channel and then
// calls the consume function passed. The passed consume
// function should never panic. Don't Panic!
func (self *Consumer) msgProcessor() {
	defer self.waitGroup.Done()
	for msg := range self.processorChannel {
		self.consumeFunc(msg)
		self.processedChannel <- true
	}
}

func (self *Consumer) listenForMsgs() {
	defer self.waitGroup.Done()
	msgsBeingProcessed := 0

    for {
        select {
			case msg := <- self.receiveChannel:
				if msgsBeingProcessed <= self.maxGoroutines {

					self.processorChannel <- msg
					msgsBeingProcessed++

				} else {
					if self.spilloverFunc == nil {
						self.logger.Logf(Warn, "Max concurrent messages (%d) reached in consumer: %s - dropping data (no spillover func set)", self.maxGoroutines, self.name)
					} else {
						// If this blocks, it will further compound problems with the consumer
						self.spilloverFunc(msg)
					}
				}
			case <- self.processedChannel: msgsBeingProcessed--
			case <- self.quitChannel: return
        }
    }
}

func (self *Consumer) Start() error {

	// Create the goroutine pool
	for idx := 0; idx < self.maxGoroutines; idx++ {
		self.waitGroup.Add(1)
		go self.msgProcessor()
	}

	// Create the message listener
	self.waitGroup.Add(1)
	go self.listenForMsgs()

	return nil
}

// This method will block until all goroutines exit. It is up to the
// caller to clear the receiveChannel.
func (self *Consumer) Stop() error {

	// Exit the listen for events select
	self.quitChannel <- true

	// If this is an unbuffered channel, close, wait for the goroutines
	// to exit and then get out of here.
	if cap(self.processorChannel) == 0 {
		close(self.processorChannel)
		self.waitGroup.Wait()
		return nil
	}

	// This is a buffered channel so we need to wait until the channel
	// is emptied (if configured to do so).
	if self.maxWaitOnStopInMs > 0 {
		start := CurrentTimeInMillis()

		for {
			if len(self.processorChannel) == 0 {
				break
			}

		 	if CurrentTimeInMillis() - start >= self.maxWaitOnStopInMs {
				break
			}

			time.Sleep(10 * time.Millisecond)
		}
	}

	// Close the processor channel and wait for all of the goroutines to return
	close(self.processorChannel)
	self.waitGroup.Wait()

	return nil
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/concourse/fly/commands"
)

var targetFlag = cli.StringFlag{
	Name:  "target, t",
	Value: "http://192.168.100.4:8080",
	Usage: "named target you have saved to your .flyrc file",
}

var taskConfigFlag = cli.StringFlag{
	Name:  "config, c",
	Usage: "task configuration file",
}

var inputFlag = cli.StringSliceFlag{
	Name:  "input, i",
	Value: &cli.StringSlice{},
}

var inputsFromPipelineFlag = cli.StringFlag{
	Name:  "inputs-from-pipeline, ifp",
	Usage: "pipeline whose job the task will base its inputs on",
}

var inputsFromJobFlag = cli.StringFlag{
	Name:  "inputs-from-job, ifj",
	Usage: "job the task will base its inputs on",
}

var insecureFlag = cli.BoolFlag{
	Name:  "insecure, k",
	Usage: "allow insecure SSL connections and transfers",
}

var excludeIgnoredFlag = cli.BoolFlag{
	Name:  "exclude-ignored, x",
	Usage: "exclude vcs-ignored files from the build's inputs",
}

var privilegedFlag = cli.BoolFlag{
	Name:  "privileged, p",
	Usage: "run the task with full privileges",
}

var pipelineFlag = cli.StringFlag{
	Name:  "pipeline, p",
	Usage: "the name of the pipeline to act upon",
}

var checkFlag = cli.StringFlag{
	Name:  "check, c",
	Usage: "name of a resource's checking container to hijack",
}

var stepTypeFlag = cli.StringFlag{
	Name:  "step-type, t",
	Usage: "type of step to hijack. one of get, put, or task.",
}

var stepNameFlag = cli.StringFlag{
	Name:  "step-name, n",
	Usage: "name of step to hijack (e.g. build, unit, resource name)",
}

var varFlag = cli.StringSliceFlag{
	Name:  "var, v",
	Value: &cli.StringSlice{},
	Usage: "variable flag that can be used for filling in template values in configuration (i.e. -var secret=key)",
}

var varFileFlag = cli.StringSliceFlag{
	Name:  "vars-from, vf",
	Value: &cli.StringSlice{},
	Usage: "variable flag that can be used for filling in template values in configuration from a YAML file",
}

var executeFlags = []cli.Flag{
	taskConfigFlag,
	inputFlag,
	excludeIgnoredFlag,
	privilegedFlag,
	inputsFromPipelineFlag,
	inputsFromJobFlag,
}

func jobFlag(verb string) cli.StringFlag {
	return cli.StringFlag{
		Name:  "job, j",
		Usage: fmt.Sprintf("if specified, %s builds of the given job", verb),
	}
}

func buildFlag(verb string) cli.StringFlag {
	return cli.StringFlag{
		Name:  "build, b",
		Usage: fmt.Sprintf("%s a specific build of a job", verb),
	}
}

var pipelineConfigFlag = cli.StringFlag{
	Name:  "config, c",
	Usage: "pipeline configuration file",
}

var jsonFlag = cli.BoolFlag{
	Name:  "json, j",
	Usage: "print config as json instead of yaml",
}

var pausedFlag = cli.BoolFlag{
	Name:  "paused",
	Usage: "should the pipeline start out as paused or unpaused (true/false)",
}

var apiFlag = cli.StringFlag{
	Name:  "api",
	Usage: "api url to target",
}

var usernameFlag = cli.StringFlag{
	Name:  "username, user",
	Usage: "username for the api",
}

var passwordFlag = cli.StringFlag{
	Name:  "password, pass",
	Usage: "password for the api",
}

var certFlag = cli.StringFlag{
	Name:  "cert",
	Usage: "directory to your cert",
}

func main() {
	app := cli.NewApp()
	app.Name = "fly"
	app.Usage = "Concourse CLI"
	app.Version = `¯\_(ツ)_/¯ (run "fly -t <target> sync")`
	app.Flags = []cli.Flag{
		insecureFlag,
		targetFlag,
	}
	app.Commands = []cli.Command{
		{
			Name:      "execute",
			ShortName: "e",
			Usage:     "Execute a build",
			Flags:     executeFlags,
			Action:    commands.Execute,
		},
		{
			Name:      "destroy-pipeline",
			ShortName: "d",
			Usage:     "destroy a pipeline",
			Action:    commands.DestroyPipeline,
		},
		takeControl("hijack"),
		takeControl("intercept"),
		{
			Name:      "watch",
			ShortName: "w",
			Usage:     "Stream a build's log",
			Flags: []cli.Flag{
				buildFlag("watches"),
				jobFlag("watches"),
				pipelineFlag,
			},
			Action: commands.Watch,
		},
		{
			Name:        "configure",
			ShortName:   "c",
			Usage:       "Update configuration",
			Description: "Specify a pipeline name to configure via `fly configure your-pipeline-name-here`",
			Flags: []cli.Flag{
				pipelineConfigFlag,
				jsonFlag,
				varFlag,
				varFileFlag,
				pausedFlag,
			},
			Action: commands.Configure,
		},
		{
			Name:      "sync",
			ShortName: "s",
			Usage:     "download and replace the current fly from the target",
			Action:    commands.Sync,
		},
		{
			Name:   "save-target",
			Usage:  "save a fly target to the .flyrc",
			Action: commands.SaveTarget,
			Flags: []cli.Flag{
				apiFlag,
				usernameFlag,
				passwordFlag,
				certFlag,
			},
		},
		{
			Name:      "checklist",
			ShortName: "l",
			Usage:     "print a Checkman checkfile for the pipeline configuration",
			Action:    commands.Checklist,
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
}

func takeControl(commandName string) cli.Command {
	return cli.Command{
		Name:      commandName,
		ShortName: "i",
		Usage:     "Execute an interactive command in a build's container",
		Flags: []cli.Flag{
			jobFlag(commandName + "s"),
			buildFlag(commandName + "s"),
			pipelineFlag,
			stepTypeFlag,
			stepNameFlag,
			checkFlag,
		},
		Action: commands.Hijack,
	}
}
<commit_msg>add usage for required args<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/concourse/fly/commands"
)

var targetFlag = cli.StringFlag{
	Name:  "target, t",
	Value: "http://192.168.100.4:8080",
	Usage: "named target you have saved to your .flyrc file",
}

var taskConfigFlag = cli.StringFlag{
	Name:  "config, c",
	Usage: "task configuration file",
}

var inputFlag = cli.StringSliceFlag{
	Name:  "input, i",
	Value: &cli.StringSlice{},
}

var inputsFromPipelineFlag = cli.StringFlag{
	Name:  "inputs-from-pipeline, ifp",
	Usage: "pipeline whose job the task will base its inputs on",
}

var inputsFromJobFlag = cli.StringFlag{
	Name:  "inputs-from-job, ifj",
	Usage: "job the task will base its inputs on",
}

var insecureFlag = cli.BoolFlag{
	Name:  "insecure, k",
	Usage: "allow insecure SSL connections and transfers",
}

var excludeIgnoredFlag = cli.BoolFlag{
	Name:  "exclude-ignored, x",
	Usage: "exclude vcs-ignored files from the build's inputs",
}

var privilegedFlag = cli.BoolFlag{
	Name:  "privileged, p",
	Usage: "run the task with full privileges",
}

var pipelineFlag = cli.StringFlag{
	Name:  "pipeline, p",
	Usage: "the name of the pipeline to act upon",
}

var checkFlag = cli.StringFlag{
	Name:  "check, c",
	Usage: "name of a resource's checking container to hijack",
}

var stepTypeFlag = cli.StringFlag{
	Name:  "step-type, t",
	Usage: "type of step to hijack. one of get, put, or task.",
}

var stepNameFlag = cli.StringFlag{
	Name:  "step-name, n",
	Usage: "name of step to hijack (e.g. build, unit, resource name)",
}

var varFlag = cli.StringSliceFlag{
	Name:  "var, v",
	Value: &cli.StringSlice{},
	Usage: "variable flag that can be used for filling in template values in configuration (i.e. -var secret=key)",
}

var varFileFlag = cli.StringSliceFlag{
	Name:  "vars-from, vf",
	Value: &cli.StringSlice{},
	Usage: "variable flag that can be used for filling in template values in configuration from a YAML file",
}

var executeFlags = []cli.Flag{
	taskConfigFlag,
	inputFlag,
	excludeIgnoredFlag,
	privilegedFlag,
	inputsFromPipelineFlag,
	inputsFromJobFlag,
}

func jobFlag(verb string) cli.StringFlag {
	return cli.StringFlag{
		Name:  "job, j",
		Usage: fmt.Sprintf("if specified, %s builds of the given job", verb),
	}
}

func buildFlag(verb string) cli.StringFlag {
	return cli.StringFlag{
		Name:  "build, b",
		Usage: fmt.Sprintf("%s a specific build of a job", verb),
	}
}

var pipelineConfigFlag = cli.StringFlag{
	Name:  "config, c",
	Usage: "pipeline configuration file",
}

var jsonFlag = cli.BoolFlag{
	Name:  "json, j",
	Usage: "print config as json instead of yaml",
}

var pausedFlag = cli.BoolFlag{
	Name:  "paused",
	Usage: "should the pipeline start out as paused or unpaused (true/false)",
}

var apiFlag = cli.StringFlag{
	Name:  "api",
	Usage: "api url to target",
}

var usernameFlag = cli.StringFlag{
	Name:  "username, user",
	Usage: "username for the api",
}

var passwordFlag = cli.StringFlag{
	Name:  "password, pass",
	Usage: "password for the api",
}

var certFlag = cli.StringFlag{
	Name:  "cert",
	Usage: "directory to your cert",
}

func main() {
	app := cli.NewApp()
	app.Name = "fly"
	app.Usage = "Concourse CLI"
	app.Version = `¯\_(ツ)_/¯ (run "fly -t <target> sync")`
	app.Flags = []cli.Flag{
		insecureFlag,
		targetFlag,
	}
	app.Commands = []cli.Command{
		{
			Name:      "execute",
			ShortName: "e",
			Usage:     "Execute a build",
			Flags:     executeFlags,
			Action:    commands.Execute,
		},
		{
			Name:      "destroy-pipeline",
			ShortName: "d",
			Usage:     "Destroy a pipeline",
			ArgsUsage: "PIPELINE_NAME",
			Action:    commands.DestroyPipeline,
		},
		takeControl("hijack"),
		takeControl("intercept"),
		{
			Name:      "watch",
			ShortName: "w",
			Usage:     "Stream a build's log",
			Flags: []cli.Flag{
				buildFlag("watches"),
				jobFlag("watches"),
				pipelineFlag,
			},
			Action: commands.Watch,
		},
		{
			Name:      "configure",
			ShortName: "c",
			Usage:     "Update or download current configuration",
			ArgsUsage: "PIPELINE_NAME",
			Flags: []cli.Flag{
				pipelineConfigFlag,
				jsonFlag,
				varFlag,
				varFileFlag,
				pausedFlag,
			},
			Action: commands.Configure,
		},
		{
			Name:      "sync",
			ShortName: "s",
			Usage:     "Download and replace the current fly from the target",
			Action:    commands.Sync,
		},
		{
			Name:      "save-target",
			Usage:     "Save a fly target to the .flyrc",
			Action:    commands.SaveTarget,
			ArgsUsage: "TARGET_NAME",
			Flags: []cli.Flag{
				apiFlag,
				usernameFlag,
				passwordFlag,
				certFlag,
			},
		},
		{
			Name:      "checklist",
			ShortName: "l",
			Usage:     "Print a Checkman checkfile for the pipeline configuration",
			ArgsUsage: "PIPELINE_NAME",
			Action:    commands.Checklist,
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
}

func takeControl(commandName string) cli.Command {
	return cli.Command{
		Name:      commandName,
		ShortName: "i",
		Usage:     "Execute an interactive command in a build's container",
		Flags: []cli.Flag{
			jobFlag(commandName + "s"),
			buildFlag(commandName + "s"),
			pipelineFlag,
			stepTypeFlag,
			stepNameFlag,
			checkFlag,
		},
		Action: commands.Hijack,
	}
}
<|endoftext|>