<commit_before>package main

import (
	"log"
	"os"
	"path/filepath"
	"github.com/nzai/Tast/config"
	"github.com/nzai/Tast/history"
	"github.com/nzai/Tast/peroidexterma"
	"github.com/nzai/Tast/stock"
	"github.com/nzai/Tast/turtle"
)

const (
	configFileName           = "config.ini"
	configLogSection         = "path"
	configLogKey             = "logpath"
	configLogDefaultFileName = "main.log"
)

func main() {

	//	当前目录
	root := filepath.Dir(os.Args[0])
	filename := filepath.Join(root, configFileName)

	//	使用所有cpu
	//	runtime.GOMAXPROCS(runtime.NumCPU() - 1)

	//	读取配置文件
	err := config.SetConfigFile(filename)
	if err != nil {
		log.Fatal(err)
		return
	}

	//	日志文件路径
	logPath := config.GetString(configLogSection, configLogKey, configLogDefaultFileName)
	err = setLogger(logPath)
	if err != nil {
		log.Fatal(err)
		return
	}

	//	更新股票信息
	err = stock.UpdateAll()
	if err != nil {
		log.Fatalf("更新股票列表发生错误:%v", err)
		return
	}

	//	更新所有股票的历史
	err = history.UpdateAll()
	if err != nil {
		log.Fatalf("更新股票历史发生错误:%v", err)
		return
	}

	//	更新所有股票的海龟指标
	err = turtle.UpdateAll()
	if err != nil {
		log.Fatalf("更新海龟指标发生错误:%v", err)
		return
	}

	//	更新所有股票的区间极值指标
	err = peroidexterma.UpdateAll()
	if err != nil {
		log.Fatalf("更新区间极值指标发生错误:%v", err)
		return
	}
}

//	设置日志输出文件
func setLogger(path string) error {

	logDir := filepath.Dir(path)
	_, err := os.Stat(logDir)
	if os.IsNotExist(err) {
		err = os.Mkdir(logDir, 0x777)
		if err != nil {
			return err
		}
	}

	file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0x777)
	if err != nil {
		return err
	}
	//defer file.Close()

	log.SetOutput(file)

	return nil
}
<commit_msg>增加海龟交易系统的测试<commit_after>package main

import (
	"log"
	"os"
	"path/filepath"

	"github.com/nzai/Tast/config"
	"github.com/nzai/Tast/history"
	"github.com/nzai/Tast/peroidexterma"
	"github.com/nzai/Tast/stock"
	"github.com/nzai/Tast/trading"
	"github.com/nzai/Tast/turtle"
)

const (
	configFileName           = "config.ini"
	configLogSection         = "path"
	configLogKey             = "logpath"
	configLogDefaultFileName = "main.log"
)

func main() {

	//	当前目录
	root := filepath.Dir(os.Args[0])
	filename := filepath.Join(root, configFileName)

	//	使用所有cpu
	//	runtime.GOMAXPROCS(runtime.NumCPU() - 1)

	//	读取配置文件
	err := config.SetConfigFile(filename)
	if err != nil {
		log.Fatal(err)
		return
	}

	//	日志文件路径
	logPath := config.GetString(configLogSection, configLogKey, configLogDefaultFileName)
	err = setLogger(logPath)
	if err != nil {
		log.Fatal(err)
		return
	}

	//	更新股票信息
	err = stock.UpdateAll()
	if err != nil {
		log.Fatalf("更新股票列表发生错误:%v", err)
		return
	}

	//	更新所有股票的历史
	err = history.UpdateAll()
	if err != nil {
		log.Fatalf("更新股票历史发生错误:%v", err)
		return
	}

	//	更新所有股票的海龟指标
	err = turtle.UpdateAll()
	if err != nil {
		log.Fatalf("更新海龟指标发生错误:%v", err)
		return
	}

	//	更新所有股票的区间极值指标
	err = peroidexterma.UpdateAll()
	if err != nil {
		log.Fatalf("更新区间极值指标发生错误:%v", err)
		return
	}

	//	测试海龟交易系统
	err = trading.TestAll()
	if err != nil {
		log.Fatalf("测试海龟交易系统发生错误:%v", err)
		return
	}
}

//	设置日志输出文件
func setLogger(path string) error {

	logDir := filepath.Dir(path)
	_, err := os.Stat(logDir)
	if os.IsNotExist(err) {
		err = os.Mkdir(logDir, 0x777)
		if err != nil {
			return err
		}
	}

	file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0x777)
	if err != nil {
		return err
	}
	//defer file.Close()

	log.SetOutput(file)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"gopkg.in/Shopify/sarama.v1"
)

const (
	VERSION = "1.0.0"
)

var (
	configFile     = flag.String("c", "config.json", "the config file")
	showVersion    = flag.Bool("v", false, "show version")
	testConfigMode = flag.Bool("t", false, "test config")
)

func main() {

	flag.Parse()

	if true == *showVersion {
		fmt.Printf("taiji v%s \n", VERSION)
		flag.Usage()
		os.Exit(0)
	}

	config, err := loadConfig(*configFile)

	if nil != err {
		log.Fatalf("load config err:%s", err.Error())
	}

	if true == *testConfigMode {
		fmt.Println("config test ok")
		fmt.Printf("config:%#v\n", config)
		os.Exit(0)
	}

	if len(config.LogFile) > 0 {
		os.MkdirAll(filepath.Dir(config.LogFile), 0777)
		f, err := os.OpenFile(config.LogFile, os.O_RDWR|os.O_CREATE, 0666)
		if nil != err {
			log.Fatalf("write log failed")
		}
		log.SetOutput(f)
		sarama.Logger = log.New(f, "[Sarama] ", log.LstdFlags)
	} else {
		sarama.Logger = log.New(os.Stdout, "[Sarama] ", log.LstdFlags)
	}

	manager := CreatePusherWorkerManager()

	var callbackConfig CallbackItemConfig
	for _, callbackConfig = range config.Callbacks {
		callback := new(PusherWorkerCallback)
		callback.RetryTimes = callbackConfig.RetryTimes
		callback.Url = callbackConfig.Url
		callback.Timeout = callbackConfig.Timeout
		callback.BypassFailed = callbackConfig.BypassFailed
		callback.FailedSleep = callbackConfig.FailedSleep
		kafkaTopics := callbackConfig.Topics
		zookeeper := callbackConfig.Zookeepers
		zkPath := callbackConfig.ZkPath

		worker := CreatePusherWorker(callback, kafkaTopics, zookeeper, zkPath)
		manager.AddWorker(worker)
	}

	err = manager.InitAll()

	if nil != err {
		log.Fatalf("init all error:%s", err.Error())
	}

	manager.WorkAll()

	go func() {
		for {
			manager.CheckAndRestart()
			time.Sleep(time.Second * 20)
		}
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGKILL)

	select {
	case <-c:
		log.Print("catch exit signal")
		manager.CloseAll()
		log.Print("exit done")
	}

}
<commit_msg>add close log<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"gopkg.in/Shopify/sarama.v1"
)

const (
	VERSION = "1.0.0"
)

var (
	configFile     = flag.String("c", "config.json", "the config file")
	showVersion    = flag.Bool("v", false, "show version")
	testConfigMode = flag.Bool("t", false, "test config")
)

func main() {

	flag.Parse()

	if true == *showVersion {
		fmt.Printf("taiji v%s \n", VERSION)
		flag.Usage()
		os.Exit(0)
	}

	config, err := loadConfig(*configFile)

	if nil != err {
		log.Fatalf("load config err:%s", err.Error())
	}

	if true == *testConfigMode {
		fmt.Println("config test ok")
		fmt.Printf("config:%#v\n", config)
		os.Exit(0)
	}

	if len(config.LogFile) > 0 {
		os.MkdirAll(filepath.Dir(config.LogFile), 0777)
		f, err := os.OpenFile(config.LogFile, os.O_RDWR|os.O_CREATE, 0666)
		defer f.Close()
		if nil != err {
			log.Fatalf("write log failed")
		}
		log.SetOutput(f)
		sarama.Logger = log.New(f, "[Sarama] ", log.LstdFlags)
	} else {
		sarama.Logger = log.New(os.Stdout, "[Sarama] ", log.LstdFlags)
	}

	manager := CreatePusherWorkerManager()

	var callbackConfig CallbackItemConfig
	for _, callbackConfig = range config.Callbacks {
		callback := new(PusherWorkerCallback)
		callback.RetryTimes = callbackConfig.RetryTimes
		callback.Url = callbackConfig.Url
		callback.Timeout = callbackConfig.Timeout
		callback.BypassFailed = callbackConfig.BypassFailed
		callback.FailedSleep = callbackConfig.FailedSleep
		kafkaTopics := callbackConfig.Topics
		zookeeper := callbackConfig.Zookeepers
		zkPath := callbackConfig.ZkPath

		worker := CreatePusherWorker(callback, kafkaTopics, zookeeper, zkPath)
		manager.AddWorker(worker)
	}

	err = manager.InitAll()

	if nil != err {
		log.Fatalf("init all error:%s", err.Error())
	}

	manager.WorkAll()

	go func() {
		for {
			manager.CheckAndRestart()
			time.Sleep(time.Second * 20)
		}
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGKILL)

	select {
	case <-c:
		log.Print("catch exit signal")
		manager.CloseAll()
		log.Print("exit done")
	}

}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc.
//
// 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.

// This program ensures source code files have copyright license headers.
// See usage with "addlicence -h".
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

const helpText = `Usage: addlicense [flags] pattern [pattern ...]

The program ensures source code files have copyright license headers
by scanning directory patterns recursively.

It modifies all source files in place and avoids adding a license header
to any file that already has one.

The pattern argument can be provided multiple times, and may also refer
to single files.

Flags:
`

var (
	holder  = flag.String("c", "Google Inc.", "copyright holder")
	license = flag.String("l", "apache", "license type: apache, bsd, mit")
	year    = flag.Int("y", time.Now().Year(), "year")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, helpText)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	data := &copyrightData{
		Year:   *year,
		Holder: *holder,
	}

	// process at most 1000 files in parallel
	ch := make(chan *file, 1000)
	done := make(chan struct{})
	go func() {
		var wg sync.WaitGroup
		for f := range ch {
			wg.Add(1)
			go func(f *file) {
				err := addLicense(f.path, f.mode, *license, data)
				if err != nil {
					log.Printf("%s: %v", f.path, err)
				}
				wg.Done()
			}(f)
		}
		wg.Wait()
		close(done)
	}()

	for _, d := range flag.Args() {
		walk(ch, d)
	}
	close(ch)
	<-done
}

type file struct {
	path string
	mode os.FileMode
}

func walk(ch chan<- *file, start string) {
	filepath.Walk(start, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf("%s error: %v", path, err)
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		ch <- &file{path, fi.Mode()}
		return nil
	})
}

func addLicense(path string, fmode os.FileMode, typ string, data *copyrightData) error {
	var lic []byte
	var err error
	switch filepath.Ext(path) {
	default:
		return nil
	case ".c", ".h":
		lic, err = prefix(typ, data, "/*", " * ", " */")
	case ".js", ".css":
		lic, err = prefix(typ, data, "/**", " * ", " */")
	case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart":
		lic, err = prefix(typ, data, "", "// ", "")
	case ".py", ".sh", ".yaml", ".yml":
		lic, err = prefix(typ, data, "", "# ", "")
	case ".el", ".lisp":
		lic, err = prefix(typ, data, "", ";; ", "")
	case ".erl":
		lic, err = prefix(typ, data, "", "% ", "")
	case ".hs":
		lic, err = prefix(typ, data, "", "-- ", "")
	case ".html", ".xml":
		lic, err = prefix(typ, data, "<!--", " ", "-->")
	case ".php":
		lic, err = prefix(typ, data, "<?php", "// ", "?>")
	}
	if err != nil || lic == nil {
		return err
	}

	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return err
	}

	line := hashBang(b)
	if len(line) > 0 {
		b = b[len(line):]
		if line[len(line)-1] != '\n' {
			line = append(line, '\n')
		}
		lic = append(line, lic...)
	}
	b = append(lic, b...)
	return ioutil.WriteFile(path, b, fmode)
}

func hashBang(b []byte) []byte {
	var line []byte
	for _, c := range b {
		line = append(line, c)
		if c == '\n' {
			break
		}
	}
	if bytes.HasPrefix(line, []byte("#!")) {
		return line
	}
	return nil
}

func hasLicense(b []byte) bool {
	n := 1000
	if len(b) < 1000 {
		n = len(b)
	}
	return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright"))
}
<commit_msg>Change default copyright holder to Google LLC<commit_after>// Copyright 2016 Google Inc.
//
// 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.

// This program ensures source code files have copyright license headers.
// See usage with "addlicence -h".
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

const helpText = `Usage: addlicense [flags] pattern [pattern ...]

The program ensures source code files have copyright license headers
by scanning directory patterns recursively.

It modifies all source files in place and avoids adding a license header
to any file that already has one.

The pattern argument can be provided multiple times, and may also refer
to single files.

Flags:
`

var (
	holder  = flag.String("c", "Google LLC", "copyright holder")
	license = flag.String("l", "apache", "license type: apache, bsd, mit")
	year    = flag.Int("y", time.Now().Year(), "year")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, helpText)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	data := &copyrightData{
		Year:   *year,
		Holder: *holder,
	}

	// process at most 1000 files in parallel
	ch := make(chan *file, 1000)
	done := make(chan struct{})
	go func() {
		var wg sync.WaitGroup
		for f := range ch {
			wg.Add(1)
			go func(f *file) {
				err := addLicense(f.path, f.mode, *license, data)
				if err != nil {
					log.Printf("%s: %v", f.path, err)
				}
				wg.Done()
			}(f)
		}
		wg.Wait()
		close(done)
	}()

	for _, d := range flag.Args() {
		walk(ch, d)
	}
	close(ch)
	<-done
}

type file struct {
	path string
	mode os.FileMode
}

func walk(ch chan<- *file, start string) {
	filepath.Walk(start, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf("%s error: %v", path, err)
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		ch <- &file{path, fi.Mode()}
		return nil
	})
}

func addLicense(path string, fmode os.FileMode, typ string, data *copyrightData) error {
	var lic []byte
	var err error
	switch filepath.Ext(path) {
	default:
		return nil
	case ".c", ".h":
		lic, err = prefix(typ, data, "/*", " * ", " */")
	case ".js", ".css":
		lic, err = prefix(typ, data, "/**", " * ", " */")
	case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart":
		lic, err = prefix(typ, data, "", "// ", "")
	case ".py", ".sh", ".yaml", ".yml":
		lic, err = prefix(typ, data, "", "# ", "")
	case ".el", ".lisp":
		lic, err = prefix(typ, data, "", ";; ", "")
	case ".erl":
		lic, err = prefix(typ, data, "", "% ", "")
	case ".hs":
		lic, err = prefix(typ, data, "", "-- ", "")
	case ".html", ".xml":
		lic, err = prefix(typ, data, "<!--", " ", "-->")
	case ".php":
		lic, err = prefix(typ, data, "<?php", "// ", "?>")
	}
	if err != nil || lic == nil {
		return err
	}

	b, err := ioutil.ReadFile(path)
	if err != nil || hasLicense(b) {
		return err
	}

	line := hashBang(b)
	if len(line) > 0 {
		b = b[len(line):]
		if line[len(line)-1] != '\n' {
			line = append(line, '\n')
		}
		lic = append(line, lic...)
	}
	b = append(lic, b...)
	return ioutil.WriteFile(path, b, fmode)
}

func hashBang(b []byte) []byte {
	var line []byte
	for _, c := range b {
		line = append(line, c)
		if c == '\n' {
			break
		}
	}
	if bytes.HasPrefix(line, []byte("#!")) {
		return line
	}
	return nil
}

func hasLicense(b []byte) bool {
	n := 1000
	if len(b) < 1000 {
		n = len(b)
	}
	return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright"))
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package main

import (
	"code.google.com/p/go.net/websocket"
	flags "github.com/jessevdk/go-flags"
	"mozilla.org/util"
	"mozilla.org/wmf"
	"mozilla.org/wmf/storage"
// Only add the following for devel.
//	_ "net/http/pprof"

	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"strings"
	"syscall"
)

var opts struct {
	ConfigFile string `short:"c" long:"config" optional:true description:"Configuration file"`
	Profile    string `long:"profile" optional:true`
	MemProfile string `long:"memprofile" optional:true`
	LogLevel   int    `short:"l" long:"loglevel" optional:true`
}

var (
	logger *util.HekaLogger
	store  *storage.Storage
)

const (
	VERSION = "0.1"
	SIGUSR1 = syscall.SIGUSR1
)

func main() {
	flags.ParseArgs(&opts, os.Args)

	// Configuration
	fmt.Printf("Opts: %v", opts)
	// defaults don't appear to work.
	if opts.ConfigFile == "" {
		opts.ConfigFile = "config.ini"
	}
	config := util.MzGetConfig(opts.ConfigFile)
	config["VERSION"] = VERSION
	if util.MzGetFlag(config, "aws.get_hostname") {
		if hostname, err := util.GetAWSPublicHostname(); err == nil {
			config["ws_hostname"] = hostname
		}
	}

	log.Printf("opts: %+v\n", opts)
	if opts.Profile != "" {
		log.Printf("Creating profile %s...\n", opts.Profile)
		f, err := os.Create(opts.Profile)
		if err != nil {
			log.Fatal("Profile creation failed:\n%s\n", err.Error())
			return
		}
		defer func() {
			log.Printf("Closing profile...\n")
			pprof.StopCPUProfile()
		}()
		pprof.StartCPUProfile(f)
	}
	if opts.MemProfile != "" {
		defer func() {
			profFile, err := os.Create(opts.MemProfile)
			if err != nil {
				log.Fatal("Memory Profile creation failed:\n%s\n", err.Error())
				return
			}
			pprof.WriteHeapProfile(profFile)
			profFile.Close()
		}()
	}

	runtime.GOMAXPROCS(runtime.NumCPU())
	logger := util.NewHekaLogger(config)
	store, err := storage.Open(config, logger)
	if err != nil {
		logger.Error("main", "FAIL", nil)
		return
	}
	handlers := wmf.NewHandler(config, logger, store)

	// Signal handler
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGHUP, SIGUSR1)

	// Rest Config
	errChan := make(chan error)
	host := util.MzGet(config, "host", "localhost")
	port := util.MzGet(config, "port", "8080")

	var RESTMux *http.ServeMux = http.DefaultServeMux
	var WSMux *http.ServeMux = http.DefaultServeMux
	var verRoot = strings.SplitN(VERSION, ".", 2)[0]

	// REST calls
	// Device calls.
	RESTMux.HandleFunc(fmt.Sprintf("/%s/register/", verRoot),
		handlers.Register)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/cmd/", verRoot),
		handlers.Cmd)
	// Web UI calls
	RESTMux.HandleFunc(fmt.Sprintf("/%s/queue/", verRoot),
		handlers.Queue)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/state/", verRoot),
		handlers.State)
	RESTMux.HandleFunc("/static/",
		handlers.Static)
	RESTMux.HandleFunc("/metrics/",
		handlers.Metrics)
	// Operations call
	RESTMux.HandleFunc("/status/",
		handlers.Status)
	WSMux.Handle(fmt.Sprintf("/%s/ws/", verRoot),
		websocket.Handler(handlers.WSSocketHandler))
	// Handle root calls as webUI
	RESTMux.HandleFunc("/",
		handlers.Index)

	logger.Info("main", "startup...",
		util.Fields{"host": host, "port": port})

	go func() {
		errChan <- http.ListenAndServe(host+":"+port, nil)
	}()

	select {
	case err := <-errChan:
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	case <-sigChan:
		logger.Info("main", "Shutting down...", nil)
	}

}
<commit_msg>removed a few logging lines<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package main

import (
	"code.google.com/p/go.net/websocket"
	flags "github.com/jessevdk/go-flags"
	"mozilla.org/util"
	"mozilla.org/wmf"
	"mozilla.org/wmf/storage"
// Only add the following for devel.
//	_ "net/http/pprof"

	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"strings"
	"syscall"
)

var opts struct {
	ConfigFile string `short:"c" long:"config" optional:true description:"Configuration file"`
	Profile    string `long:"profile" optional:true`
	MemProfile string `long:"memprofile" optional:true`
	LogLevel   int    `short:"l" long:"loglevel" optional:true`
}

var (
	logger *util.HekaLogger
	store  *storage.Storage
)

const (
	VERSION = "0.1"
	SIGUSR1 = syscall.SIGUSR1
)

func main() {
	flags.ParseArgs(&opts, os.Args)

	// Configuration
	// defaults don't appear to work.
	if opts.ConfigFile == "" {
		opts.ConfigFile = "config.ini"
	}
	config := util.MzGetConfig(opts.ConfigFile)
	config["VERSION"] = VERSION
	if util.MzGetFlag(config, "aws.get_hostname") {
		if hostname, err := util.GetAWSPublicHostname(); err == nil {
			config["ws_hostname"] = hostname
		}
	}

	if opts.Profile != "" {
		log.Printf("Creating profile %s...\n", opts.Profile)
		f, err := os.Create(opts.Profile)
		if err != nil {
			log.Fatal("Profile creation failed:\n%s\n", err.Error())
			return
		}
		defer func() {
			log.Printf("Closing profile...\n")
			pprof.StopCPUProfile()
		}()
		pprof.StartCPUProfile(f)
	}
	if opts.MemProfile != "" {
		defer func() {
			profFile, err := os.Create(opts.MemProfile)
			if err != nil {
				log.Fatal("Memory Profile creation failed:\n%s\n", err.Error())
				return
			}
			pprof.WriteHeapProfile(profFile)
			profFile.Close()
		}()
	}

	runtime.GOMAXPROCS(runtime.NumCPU())
	logger := util.NewHekaLogger(config)
	store, err := storage.Open(config, logger)
	if err != nil {
		logger.Error("main", "FAIL", nil)
		return
	}
	handlers := wmf.NewHandler(config, logger, store)

	// Signal handler
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGHUP, SIGUSR1)

	// Rest Config
	errChan := make(chan error)
	host := util.MzGet(config, "host", "localhost")
	port := util.MzGet(config, "port", "8080")

	var RESTMux *http.ServeMux = http.DefaultServeMux
	var WSMux *http.ServeMux = http.DefaultServeMux
	var verRoot = strings.SplitN(VERSION, ".", 2)[0]

	// REST calls
	// Device calls.
	RESTMux.HandleFunc(fmt.Sprintf("/%s/register/", verRoot),
		handlers.Register)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/cmd/", verRoot),
		handlers.Cmd)
	// Web UI calls
	RESTMux.HandleFunc(fmt.Sprintf("/%s/queue/", verRoot),
		handlers.Queue)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/state/", verRoot),
		handlers.State)
	RESTMux.HandleFunc("/static/",
		handlers.Static)
	RESTMux.HandleFunc("/metrics/",
		handlers.Metrics)
	// Operations call
	RESTMux.HandleFunc("/status/",
		handlers.Status)
	WSMux.Handle(fmt.Sprintf("/%s/ws/", verRoot),
		websocket.Handler(handlers.WSSocketHandler))
	// Handle root calls as webUI
	RESTMux.HandleFunc("/",
		handlers.Index)

	logger.Info("main", "startup...",
		util.Fields{"host": host, "port": port})

	go func() {
		errChan <- http.ListenAndServe(host+":"+port, nil)
	}()

	select {
	case err := <-errChan:
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	case <-sigChan:
		logger.Info("main", "Shutting down...", nil)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"go/build"
	"log"
	"math/rand"
	"time"
	"github.com/esimov/diagram/canvas"
	"github.com/esimov/diagram/io"
	"github.com/esimov/diagram/ui"
	"github.com/fogleman/imview"
)

var defaultFontFile = build.Default.GOPATH + "/src/github.com/esimov/diagram" + "/font/gloriahallelujah.ttf"

var (
	source      = flag.String("in", "", "Source")
	destination = flag.String("out", "", "Destination")
	fontpath    = flag.String("font", defaultFontFile, "path to font file")
	preview     = flag.Bool("preview", true, "Show the preview window")
)

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

	flag.Parse()
	// If filenames specified on the commandline generate diagram directly with command line tool.
	if (*source != "") && (*destination != "") {
		input := string(io.ReadFile(*source))

		err := canvas.DrawDiagram(input, *destination, *fontpath)
		if err != nil {
			log.Fatal("Error on converting the ascii art to hand drawn diagrams!")
		} else if *preview {
			image, _ := imview.LoadImage(*destination)
			view := imview.ImageToRGBA(image)
			imview.Show(view)
		}
	} else {
		ui.InitApp(*fontpath)
	}
}
<commit_msg>Godoc description<commit_after>// Diagram is a CLI tool to generate hand drawn diagrams from ASCII arts.
//
// It's a full featured CLI application which converts the ASCII text into hand drawn diagrams.

package main

import (
	"flag"
	"go/build"
	"log"
	"math/rand"
	"time"

	"github.com/esimov/diagram/canvas"
	"github.com/esimov/diagram/io"
	"github.com/esimov/diagram/ui"
	"github.com/fogleman/imview"
)

var defaultFontFile = build.Default.GOPATH + "/src/github.com/esimov/diagram" + "/font/gloriahallelujah.ttf"

var (
	source      = flag.String("in", "", "Source")
	destination = flag.String("out", "", "Destination")
	fontPath    = flag.String("font", defaultFontFile, "Path to the font file")
	preview     = flag.Bool("preview", true, "Show the preview window")
)

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

	flag.Parse()
	// If filenames specified on the commandline generate diagram directly with command line tool.
	if (*source != "") && (*destination != "") {
		input := string(io.ReadFile(*source))

		err := canvas.DrawDiagram(input, *destination, *fontPath)
		if err != nil {
			log.Fatal("Error on converting the ascii art to hand drawn diagrams!")
		} else if *preview {
			image, _ := imview.LoadImage(*destination)
			view := imview.ImageToRGBA(image)
			imview.Show(view)
		}
	} else {
		ui.InitApp(*fontPath)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strings"
	"unsafe"

	"github.com/jangler/edit"
	"github.com/veandco/go-sdl2/sdl"
	"github.com/veandco/go-sdl2/sdl_ttf"
)

const version = "0.0.0"

const (
	insertMark = iota // ID of the cursor/insertion mark
	selMark           // ID of the selection anchor mark
)

// getFont loads the default TTF from memory and returns it.
func getFont() *ttf.Font {
	data, err := Asset("data/DejaVuSansMono.ttf")
	if err != nil {
		log.Fatal(err)
	}
	rw := sdl.RWFromMem(unsafe.Pointer(&data[0]), len(data))
	font, err := ttf.OpenFontRW(rw, 1, 12)
	if err != nil {
		log.Fatal(err)
	}
	font.SetHinting(ttf.HINTING_MONO)
	return font
}

// initFlag processes command-line flags and arguments.
func initFlag() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [<option> ...] [<file>]\n",
			os.Args[0])
		fmt.Fprintln(os.Stderr, "\nOptions:")
		flag.PrintDefaults()
	}
	versionFlag := flag.Bool("version", false, "print version information "+
		"and exit")

	flag.Parse()

	if *versionFlag {
		fmt.Printf("%s version %s %s/%s\n", os.Args[0], version, runtime.GOOS,
			runtime.GOARCH)
		os.Exit(0)
	}

	if flag.NArg() > 1 {
		flag.Usage()
		os.Exit(1)
	}
}

// openFile attempts to open the file given by path and return a new buffer
// containing the contents of that file. If an error is encountered, it returns
// a nil buffer and the error instead.
func openFile(path string) (*edit.Buffer, error) {
	contents, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	buf := edit.NewBuffer()
	buf.Insert(buf.End(), string(contents))
	if buf.Get(buf.ShiftIndex(buf.End(), -1), buf.End()) == "\n" {
		buf.Delete(buf.ShiftIndex(buf.End(), -1), buf.End())
	}
	buf.ResetModified()
	buf.ResetUndo()
	return buf, nil
}

// SetSyntax automatically sets the syntax rules for p.
func (p *Pane) SetSyntax() {
	if strings.HasSuffix(p.Title, ".c") || strings.HasSuffix(p.Title, ".h") {
		p.Buffer.SetSyntax(cRules())
	} else if strings.HasSuffix(p.Title, ".go") {
		p.Buffer.SetSyntax(goRules())
	} else if strings.HasSuffix(p.Title, ".json") {
		p.Buffer.SetSyntax(jsonRules())
	} else if strings.ToLower(p.Title) == "makefile" {
		p.Buffer.SetSyntax(makefileRules())
	} else if strings.HasSuffix(p.Title, ".py") {
		p.Buffer.SetSyntax(pythonRules())
	} else if strings.HasSuffix(p.Title, ".sh") {
		p.Buffer.SetSyntax(pythonRules())
	} else {
		firstLine := p.Buffer.Get(edit.Index{1, 0}, edit.Index{1, 1 << 30})
		if strings.HasPrefix(firstLine, "#!") {
			if strings.Contains(firstLine, "python") {
				p.Buffer.SetSyntax(pythonRules())
			} else if strings.Contains(firstLine, "sh") {
				p.Buffer.SetSyntax(bashRules())
			} else {
				p.Buffer.SetSyntax([]edit.Rule{})
			}
		} else {
			p.Buffer.SetSyntax([]edit.Rule{})
		}
	}
}

func main() {
	initFlag()
	log.SetFlags(log.Lshortfile)

	runtime.LockOSThread()
	sdl.Init(sdl.INIT_VIDEO)
	defer sdl.Quit()
	if err := ttf.Init(); err != nil {
		log.Fatal(err)
	}
	defer ttf.Quit()

	font := getFont()
	var err error
	fontWidth, _, err = font.SizeUTF8("0")
	if err != nil {
		log.Fatal(err)
	}

	var pane *Pane
	var arg, status string
	if flag.NArg() == 0 || flag.Arg(0) == "" {
		arg = os.DevNull
	} else {
		arg = flag.Arg(0)
	}
	if buf, err := openFile(arg); err == nil {
		status = fmt.Sprintf(`Opened "%s".`, minPath(arg))
		pane = &Pane{buf, minPath(arg), 4, 80, 25}
	} else {
		status = fmt.Sprintf(`New file: "%s".`, minPath(arg))
		pane = &Pane{edit.NewBuffer(), minPath(arg), 4, 80, 25}
	}
	pane.SetTabWidth(4)
	pane.SetSyntax()
	pane.Mark(edit.Index{1, 0}, insertMark)
	pane.Mark(edit.Index{1, 0}, selMark)

	win := createWindow(minPath(arg), font)
	defer win.Destroy()

	w, h := win.GetSize()
	resize(pane, font, w, h)
	eventLoop(pane, status, font, win)
}
<commit_msg>Use default font hinting<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strings"
	"unsafe"

	"github.com/jangler/edit"
	"github.com/veandco/go-sdl2/sdl"
	"github.com/veandco/go-sdl2/sdl_ttf"
)

const version = "0.0.0"

const (
	insertMark = iota // ID of the cursor/insertion mark
	selMark           // ID of the selection anchor mark
)

// getFont loads the default TTF from memory and returns it.
func getFont() *ttf.Font {
	data, err := Asset("data/DejaVuSansMono.ttf")
	if err != nil {
		log.Fatal(err)
	}
	rw := sdl.RWFromMem(unsafe.Pointer(&data[0]), len(data))
	font, err := ttf.OpenFontRW(rw, 1, 12)
	if err != nil {
		log.Fatal(err)
	}
	return font
}

// initFlag processes command-line flags and arguments.
func initFlag() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [<option> ...] [<file>]\n",
			os.Args[0])
		fmt.Fprintln(os.Stderr, "\nOptions:")
		flag.PrintDefaults()
	}
	versionFlag := flag.Bool("version", false, "print version information "+
		"and exit")

	flag.Parse()

	if *versionFlag {
		fmt.Printf("%s version %s %s/%s\n", os.Args[0], version, runtime.GOOS,
			runtime.GOARCH)
		os.Exit(0)
	}

	if flag.NArg() > 1 {
		flag.Usage()
		os.Exit(1)
	}
}

// openFile attempts to open the file given by path and return a new buffer
// containing the contents of that file. If an error is encountered, it returns
// a nil buffer and the error instead.
func openFile(path string) (*edit.Buffer, error) {
	contents, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	buf := edit.NewBuffer()
	buf.Insert(buf.End(), string(contents))
	if buf.Get(buf.ShiftIndex(buf.End(), -1), buf.End()) == "\n" {
		buf.Delete(buf.ShiftIndex(buf.End(), -1), buf.End())
	}
	buf.ResetModified()
	buf.ResetUndo()
	return buf, nil
}

// SetSyntax automatically sets the syntax rules for p.
func (p *Pane) SetSyntax() {
	if strings.HasSuffix(p.Title, ".c") || strings.HasSuffix(p.Title, ".h") {
		p.Buffer.SetSyntax(cRules())
	} else if strings.HasSuffix(p.Title, ".go") {
		p.Buffer.SetSyntax(goRules())
	} else if strings.HasSuffix(p.Title, ".json") {
		p.Buffer.SetSyntax(jsonRules())
	} else if strings.ToLower(p.Title) == "makefile" {
		p.Buffer.SetSyntax(makefileRules())
	} else if strings.HasSuffix(p.Title, ".py") {
		p.Buffer.SetSyntax(pythonRules())
	} else if strings.HasSuffix(p.Title, ".sh") {
		p.Buffer.SetSyntax(pythonRules())
	} else {
		firstLine := p.Buffer.Get(edit.Index{1, 0}, edit.Index{1, 1 << 30})
		if strings.HasPrefix(firstLine, "#!") {
			if strings.Contains(firstLine, "python") {
				p.Buffer.SetSyntax(pythonRules())
			} else if strings.Contains(firstLine, "sh") {
				p.Buffer.SetSyntax(bashRules())
			} else {
				p.Buffer.SetSyntax([]edit.Rule{})
			}
		} else {
			p.Buffer.SetSyntax([]edit.Rule{})
		}
	}
}

func main() {
	initFlag()
	log.SetFlags(log.Lshortfile)

	runtime.LockOSThread()
	sdl.Init(sdl.INIT_VIDEO)
	defer sdl.Quit()
	if err := ttf.Init(); err != nil {
		log.Fatal(err)
	}
	defer ttf.Quit()

	font := getFont()
	var err error
	fontWidth, _, err = font.SizeUTF8("0")
	if err != nil {
		log.Fatal(err)
	}

	var pane *Pane
	var arg, status string
	if flag.NArg() == 0 || flag.Arg(0) == "" {
		arg = os.DevNull
	} else {
		arg = flag.Arg(0)
	}
	if buf, err := openFile(arg); err == nil {
		status = fmt.Sprintf(`Opened "%s".`, minPath(arg))
		pane = &Pane{buf, minPath(arg), 4, 80, 25}
	} else {
		status = fmt.Sprintf(`New file: "%s".`, minPath(arg))
		pane = &Pane{edit.NewBuffer(), minPath(arg), 4, 80, 25}
	}
	pane.SetTabWidth(4)
	pane.SetSyntax()
	pane.Mark(edit.Index{1, 0}, insertMark)
	pane.Mark(edit.Index{1, 0}, selMark)

	win := createWindow(minPath(arg), font)
	defer win.Destroy()

	w, h := win.GetSize()
	resize(pane, font, w, h)
	eventLoop(pane, status, font, win)
}
<|endoftext|>


<commit_before>package main

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

	log "github.com/Sirupsen/logrus"
	"github.com/google/go-github/github"
)

var isDelete = flag.Bool("delete", false, "delete all tweets before posting")
var logLevel = flag.String("loglevel", "warning", "log level (panic, fatal, error, warn or warning, info, debug)")
var lastPosted int

func updateTwitter(g *Github, t *Twitter) {
	// tweetCutie posts cutie from pull request pull to twitter
	tweetCutie := func(pull *github.Issue) error {
		if pull.Body == nil {
			return nil
		}
		cutie, err := GetCutieFromPull(pull)
		if err != nil {
			switch err {
			case errImageNotFound:

			case errIsScreenshot:
				log.WithFields(log.Fields{"number": *pull.Number, "URL": *pull.HTMLURL}).Warn("Screenshot detected")
				t.Notify(fmt.Sprintf("Screenshot detected: %s", *pull.HTMLURL))
				lastPosted = *pull.Number
			default:
				log.WithFields(log.Fields{"since": lastPosted + 1}).WithError(err).Error("For pull requests since")
				t.Notify(fmt.Sprintf("Cannot get cutie from pull request %d, %s: %s", *pull.Number, *pull.HTMLURL, err))
			}
			return nil
		}
		if cutie != "" {
			log.WithFields(log.Fields{"number": *pull.Number, "URL": *pull.HTMLURL}).Info("Cutie")
			msg := fmt.Sprintf("%s #dockercuties #docker", *pull.HTMLURL)
			if err := t.PostToTwitter(cutie, msg); err != nil {
				t.Notify(fmt.Sprintf("Cannot post tweet: %s", err))
				return err
			}
			lastPosted = *pull.Number
		}
		return nil
	}
	log.WithFields(log.Fields{"number": lastPosted}).Debug("Last posted")
	if lastPosted > 0 {
		if err := g.PullsSinceFunc(lastPosted+1, tweetCutie); err != nil {
			if strings.Contains(err.Error(), "404 Not Found") {
				log.WithFields(log.Fields{"Owner": Owner, "Repo": Repo, "number": lastPosted + 1}).Debug("Issue not found")
				return
			}
			log.WithFields(log.Fields{"since": lastPosted + 1}).WithError(err).Error("For pull requests since")
			t.Notify(fmt.Sprintf("Error for pull requests since %d: %s", lastPosted+1, err))
			return
		}
	} else {
		if err := g.PullsSinceFunc(StartCutiePullReq, tweetCutie); err != nil {
			log.WithFields(log.Fields{"since": StartCutiePullReq}).WithError(err).Error("For pull requests since")
			t.Notify(fmt.Sprintf("Error for pull requests since %d: %s", StartCutiePullReq, err))
			return
		}
	}
}

func main() {
	flag.Parse()
	lvl, err := log.ParseLevel(*logLevel)
	if err != nil {
		log.WithFields(log.Fields{"log level": *logLevel}).WithError(err).Fatal("Cannot parse log level")
	}
	log.SetLevel(lvl)

	tokens, err := LoadTokens()
	log.Info("Tokens are loaded")
	if err != nil {
		log.WithError(err).Fatal("Cannot parse tokens")
		return
	}

	t := NewTwitter(tokens.twitter)
	log.Info("Connect to twitter")
	gh := NewGithub(tokens.github)
	log.Info("Connect to github")

	if *isDelete {
		if err := t.DeleteAllTweets(TwitterUser); err != nil {
			log.WithFields(log.Fields{"User": TwitterUser}).WithError(err).Error("Cannot delete all tweets")
			t.Notify(fmt.Sprintf("Cannot delete all tweets: %s", err))
			return
		}
	}

	// // Single post by number
	// n := 32085
	// if err = gh.PullFunc(n, tweetCutie); err != nil {
	// 	log.WithFields(log.Fields{"number": n}).WithError(err).Error("For pull request")
	// 	return
	// }
	// return

	lastPosted = t.LastPostedPull()

	for range time.Tick(60 * time.Second) {
		updateTwitter(gh, t)
	}
}
<commit_msg>Add pull request number to screenshot log<commit_after>package main

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

	log "github.com/Sirupsen/logrus"
	"github.com/google/go-github/github"
)

var isDelete = flag.Bool("delete", false, "delete all tweets before posting")
var logLevel = flag.String("loglevel", "warning", "log level (panic, fatal, error, warn or warning, info, debug)")
var lastPosted int

func updateTwitter(g *Github, t *Twitter) {
	// tweetCutie posts cutie from pull request pull to twitter
	tweetCutie := func(pull *github.Issue) error {
		if pull.Body == nil {
			return nil
		}
		cutie, err := GetCutieFromPull(pull)
		if err != nil {
			switch err {
			case errImageNotFound:

			case errIsScreenshot:
				log.WithFields(log.Fields{"number": *pull.Number, "URL": *pull.HTMLURL}).Warn("Screenshot detected")
				t.Notify(fmt.Sprintf("%d Screenshot detected: %s", *pull.Number, *pull.HTMLURL))
				lastPosted = *pull.Number
			default:
				log.WithFields(log.Fields{"since": lastPosted + 1}).WithError(err).Error("For pull requests since")
				t.Notify(fmt.Sprintf("Cannot get cutie from pull request %d, %s: %s", *pull.Number, *pull.HTMLURL, err))
			}
			return nil
		}
		if cutie != "" {
			log.WithFields(log.Fields{"number": *pull.Number, "URL": *pull.HTMLURL}).Info("Cutie")
			msg := fmt.Sprintf("%s #dockercuties #docker", *pull.HTMLURL)
			if err := t.PostToTwitter(cutie, msg); err != nil {
				t.Notify(fmt.Sprintf("Cannot post tweet: %s", err))
				return err
			}
			lastPosted = *pull.Number
		}
		return nil
	}
	log.WithFields(log.Fields{"number": lastPosted}).Debug("Last posted")
	if lastPosted > 0 {
		if err := g.PullsSinceFunc(lastPosted+1, tweetCutie); err != nil {
			if strings.Contains(err.Error(), "404 Not Found") {
				log.WithFields(log.Fields{"Owner": Owner, "Repo": Repo, "number": lastPosted + 1}).Debug("Issue not found")
				return
			}
			log.WithFields(log.Fields{"since": lastPosted + 1}).WithError(err).Error("For pull requests since")
			t.Notify(fmt.Sprintf("Error for pull requests since %d: %s", lastPosted+1, err))
			return
		}
	} else {
		if err := g.PullsSinceFunc(StartCutiePullReq, tweetCutie); err != nil {
			log.WithFields(log.Fields{"since": StartCutiePullReq}).WithError(err).Error("For pull requests since")
			t.Notify(fmt.Sprintf("Error for pull requests since %d: %s", StartCutiePullReq, err))
			return
		}
	}
}

func main() {
	flag.Parse()
	lvl, err := log.ParseLevel(*logLevel)
	if err != nil {
		log.WithFields(log.Fields{"log level": *logLevel}).WithError(err).Fatal("Cannot parse log level")
	}
	log.SetLevel(lvl)

	tokens, err := LoadTokens()
	log.Info("Tokens are loaded")
	if err != nil {
		log.WithError(err).Fatal("Cannot parse tokens")
		return
	}

	t := NewTwitter(tokens.twitter)
	log.Info("Connect to twitter")
	gh := NewGithub(tokens.github)
	log.Info("Connect to github")

	if *isDelete {
		if err := t.DeleteAllTweets(TwitterUser); err != nil {
			log.WithFields(log.Fields{"User": TwitterUser}).WithError(err).Error("Cannot delete all tweets")
			t.Notify(fmt.Sprintf("Cannot delete all tweets: %s", err))
			return
		}
	}

	// // Single post by number
	// n := 32435
	// if err = gh.PullFunc(n, tweetCutie); err != nil {
	// 	log.WithFields(log.Fields{"number": n}).WithError(err).Error("For pull request")
	// 	return
	// }
	// return

	lastPosted = t.LastPostedPull()

	for range time.Tick(60 * time.Second) {
		updateTwitter(gh, t)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/exitcodezero/picloud/config"
	"github.com/exitcodezero/picloud/routes"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime/pprof"
)

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func init() {
	router := routes.Router()
	http.Handle("/", router)
}

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

	host := fmt.Sprintf("0.0.0.0:%s", config.Port)
	if config.UseTLS == "" {
		err := http.ListenAndServe(host, nil)
		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}
	} else {
		err := http.ListenAndServeTLS(host, config.CertFile, config.KeyFile, nil)
		if err != nil {
			log.Fatal("ListenAndServeTLS: ", err)
		}
	}

}
<commit_msg>gofmt<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/exitcodezero/picloud/config"
	"github.com/exitcodezero/picloud/routes"
	"log"
	"net/http"
	"os"
	"runtime/pprof"
)

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func init() {
	router := routes.Router()
	http.Handle("/", router)
}

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

	host := fmt.Sprintf("0.0.0.0:%s", config.Port)
	if config.UseTLS == "" {
		err := http.ListenAndServe(host, nil)
		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}
	} else {
		err := http.ListenAndServeTLS(host, config.CertFile, config.KeyFile, nil)
		if err != nil {
			log.Fatal("ListenAndServeTLS: ", err)
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"sync"

	"gopkg.in/yaml.v2"

	mtr "github.com/Shinzu/go-mtr"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/version"
)

type Exporter struct {
	mutex              sync.Mutex
	sent               *prometheus.CounterVec
	received           *prometheus.CounterVec
	dropped            *prometheus.CounterVec
	lost               *prometheus.CounterVec
	latency            *prometheus.SummaryVec
	routeChanges       *prometheus.CounterVec
	destinationChanges *prometheus.CounterVec
	failed             *prometheus.CounterVec
	lastDest           map[string]net.IP
	lastRoute          map[string][]net.IP
}

type Config struct {
	Arguments []string `yaml:"args"`
	Hosts     []Host   `yaml:"hosts"`
}

type Host struct {
	Name  string `yaml:"name"`
	Alias string `yaml:"alias"`
}

type TargetFeedback struct {
	Target string
	Alias  string
	Hosts  []*mtr.Host
}

var config Config

const (
	Namespace = "mtr"
)

func NewExporter() *Exporter {
	var (
		alias        = "alias"
		server       = "server"
		hop_id       = "hop_id"
		hop_ip       = "hop_ip"
		previousDest = "previous"
		currentDest  = "current"
	)

	return &Exporter{
		sent: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "sent",
				Help:      "packets sent",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		received: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "received",
				Help:      "packets received",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		dropped: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "dropped",
				Help:      "packets dropped",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		lost: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "lost",
				Help:      "packets lost",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		latency: prometheus.NewSummaryVec(
			prometheus.SummaryOpts{
				Namespace: Namespace,
				Name:      "latency",
				Help:      "packet latency in microseconds",
				Objectives: map[float64]float64{
					0.5:  0.05,
					0.9:  0.01,
					0.99: 0.001,
				},
				MaxAge:     prometheus.DefMaxAge,
				AgeBuckets: prometheus.DefAgeBuckets,
				BufCap:     prometheus.DefBufCap,
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		routeChanges: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "route_changes",
				Help:      "route changes",
			},
			[]string{alias, server, hop_id},
		),
		destinationChanges: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "destination_changes",
				Help:      "Number of times the destination IP has changed",
			},
			[]string{alias, server, previousDest, currentDest},
		),
		failed: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "failed",
				Help:      "MTR runs failed",
			},
			[]string{alias, server},
		),
		lastDest:  make(map[string]net.IP, len(config.Hosts)),
		lastRoute: make(map[string][]net.IP, len(config.Hosts)),
	}
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	e.sent.Describe(ch)
	e.received.Describe(ch)
	e.dropped.Describe(ch)
	e.lost.Describe(ch)
	e.latency.Describe(ch)
	e.routeChanges.Describe(ch)
	e.destinationChanges.Describe(ch)
	e.failed.Describe(ch)
}

func min(a int, b int) int {
	if a > b {
		return b
	}
	return a
}

func (e *Exporter) collect() error {
	for {
		results := make(chan *TargetFeedback)
		wg := &sync.WaitGroup{}
		wg.Add(len(config.Hosts))

		for w, host := range config.Hosts {
			go func(w int, host Host) {
				log.Infoln("worker", w, "processing job", host.Name, "aliased as", host.Alias)
				err := worker(w, host, results, wg)
				if err != nil {
					log.Errorf("worker %d failed job %v aliased as %v: %v\n", w, host.Name, host.Alias, err)
					e.failed.WithLabelValues(host.Alias, host.Name).Inc()
				} else {
					log.Infoln("worker", w, "finished job", host.Name, "aliased as", host.Alias)
				}
			}(w, host)
		}

		go func() {
			wg.Wait()
			close(results)
		}()

		for tf := range results {
			route := make([]net.IP, len(tf.Hosts))
			destination := tf.Hosts[len(tf.Hosts)-1].IP
			for i, host := range tf.Hosts {
				route[i] = host.IP
				e.sent.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(float64(host.Sent))
				e.received.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(float64(host.Received))
				e.dropped.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(float64(host.Dropped))
				e.lost.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(host.LostPercent * float64(host.Sent))
				e.latency.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Observe(host.Mean)
			}
			for i := 0; i < min(len(route), len(e.lastRoute[tf.Alias])); i++ {
				if e.lastRoute[tf.Alias] != nil && !reflect.DeepEqual(route[i], e.lastRoute[tf.Alias][i]) {
					e.routeChanges.WithLabelValues(tf.Alias, tf.Target, fmt.Sprintf("%d", i)).Inc()
					e.lastRoute[tf.Alias] = route
				}
			}
			if e.lastDest[tf.Alias] != nil && !reflect.DeepEqual(destination, e.lastDest[tf.Alias]) {
				e.destinationChanges.WithLabelValues(tf.Alias, tf.Target, e.lastDest[tf.Alias].String(), destination.String()).Inc()
				e.lastDest[tf.Alias] = destination
			}
		}
	}
}

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	e.sent.Collect(ch)
	e.received.Collect(ch)
	e.dropped.Collect(ch)
	e.lost.Collect(ch)
	e.latency.Collect(ch)
	e.routeChanges.Collect(ch)
	e.destinationChanges.Collect(ch)
	e.failed.Collect(ch)
	return
}

func trace(host Host, results chan<- *TargetFeedback) error {
	// run MTR and wait for it to complete
	a := mtr.New(1, host.Name, config.Arguments...)
	<-a.Done

	// output result
	if a.Error == nil {
		results <- &TargetFeedback{
			Target: host.Name,
			Alias:  host.Alias,
			Hosts:  a.Hosts,
		}
	}
	return a.Error
}

func worker(id int, host Host, results chan<- *TargetFeedback, wg *sync.WaitGroup) error {
	defer wg.Done()
	return trace(host, results)
}

func main() {
	var (
		configFile    = flag.String("config.file", "mtr.yaml", "MTR exporter configuration file.")
		listenAddress = flag.String("web.listen-address", ":9116", "The address to listen on for HTTP requests.")
		showVersion   = flag.Bool("version", false, "Print version information.")
	)

	flag.Parse()

	if *showVersion {
		fmt.Fprintln(os.Stdout, version.Print("mtr_exporter"))
		os.Exit(0)
	}

	log.Infoln("Starting mtr_exporter", version.Info())
	log.Infoln("Build context", version.BuildContext())

	yamlFile, err := ioutil.ReadFile(*configFile)

	if err != nil {
		log.Fatalf("Error reading config file: %s", err)
	}

	err = yaml.Unmarshal(yamlFile, &config)
	if err != nil {
		log.Fatalf("Error parsing config file: %s", err)
	}

	prometheus.MustRegister(version.NewCollector("mtr_exporter"))
	exporter := NewExporter()
	prometheus.MustRegister(exporter)

	go exporter.collect()

	http.Handle("/metrics", prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
            <title>MTR Exporter</title>

MTR Exporter

Metrics

            `))
	})

	log.Infoln("Listening on", *listenAddress)
	if err := http.ListenAndServe(*listenAddress, nil); err != nil {
		log.Fatalf("Error starting HTTP server: %s", err)
	}
}
<commit_msg>Cover more cases for route changes and avoid duplication with destination changes<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"sync"

	"gopkg.in/yaml.v2"

	mtr "github.com/Shinzu/go-mtr"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/version"
)

type Exporter struct {
	mutex              sync.Mutex
	sent               *prometheus.CounterVec
	received           *prometheus.CounterVec
	dropped            *prometheus.CounterVec
	lost               *prometheus.CounterVec
	latency            *prometheus.SummaryVec
	routeChanges       *prometheus.CounterVec
	destinationChanges *prometheus.CounterVec
	failed             *prometheus.CounterVec
	lastDest           map[string]net.IP
	lastRoute          map[string][]net.IP
}

type Config struct {
	Arguments []string `yaml:"args"`
	Hosts     []Host   `yaml:"hosts"`
}

type Host struct {
	Name  string `yaml:"name"`
	Alias string `yaml:"alias"`
}

type TargetFeedback struct {
	Target string
	Alias  string
	Hosts  []*mtr.Host
}

var config Config

const (
	Namespace = "mtr"
)

func NewExporter() *Exporter {
	var (
		alias        = "alias"
		server       = "server"
		hop_id       = "hop_id"
		hop_ip       = "hop_ip"
		previousDest = "previous"
		currentDest  = "current"
	)

	return &Exporter{
		sent: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "sent",
				Help:      "packets sent",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		received: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "received",
				Help:      "packets received",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		dropped: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "dropped",
				Help:      "packets dropped",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		lost: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "lost",
				Help:      "packets lost",
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		latency: prometheus.NewSummaryVec(
			prometheus.SummaryOpts{
				Namespace: Namespace,
				Name:      "latency",
				Help:      "packet latency in microseconds",
				Objectives: map[float64]float64{
					0.5:  0.05,
					0.9:  0.01,
					0.99: 0.001,
				},
				MaxAge:     prometheus.DefMaxAge,
				AgeBuckets: prometheus.DefAgeBuckets,
				BufCap:     prometheus.DefBufCap,
			},
			[]string{alias, server, hop_id, hop_ip},
		),
		routeChanges: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "route_changes",
				Help:      "route changes",
			},
			[]string{alias, server, hop_id},
		),
		destinationChanges: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "destination_changes",
				Help:      "Number of times the destination IP has changed",
			},
			[]string{alias, server, previousDest, currentDest},
		),
		failed: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "failed",
				Help:      "MTR runs failed",
			},
			[]string{alias, server},
		),
		lastDest:  make(map[string]net.IP, len(config.Hosts)),
		lastRoute: make(map[string][]net.IP, len(config.Hosts)),
	}
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	e.sent.Describe(ch)
	e.received.Describe(ch)
	e.dropped.Describe(ch)
	e.lost.Describe(ch)
	e.latency.Describe(ch)
	e.routeChanges.Describe(ch)
	e.destinationChanges.Describe(ch)
	e.failed.Describe(ch)
}

func min(a int, b int) int {
	if a > b {
		return b
	}
	return a
}

func (e *Exporter) collect() error {
	for {
		results := make(chan *TargetFeedback)
		wg := &sync.WaitGroup{}
		wg.Add(len(config.Hosts))

		for w, host := range config.Hosts {
			go func(w int, host Host) {
				log.Infoln("worker", w, "processing job", host.Name, "aliased as", host.Alias)
				err := worker(w, host, results, wg)
				if err != nil {
					log.Errorf("worker %d failed job %v aliased as %v: %v\n", w, host.Name, host.Alias, err)
					e.failed.WithLabelValues(host.Alias, host.Name).Inc()
				} else {
					log.Infoln("worker", w, "finished job", host.Name, "aliased as", host.Alias)
				}
			}(w, host)
		}

		go func() {
			wg.Wait()
			close(results)
		}()

		for tf := range results {
			route := make([]net.IP, len(tf.Hosts))
			destination := tf.Hosts[len(tf.Hosts)-1].IP
			for i, host := range tf.Hosts {
				route[i] = host.IP
				e.sent.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(float64(host.Sent))
				e.received.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(float64(host.Received))
				e.dropped.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(float64(host.Dropped))
				e.lost.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Add(host.LostPercent * float64(host.Sent))
				e.latency.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(host.Hop), host.IP.String()).Observe(host.Mean)
			}
			if e.lastRoute[tf.Alias] != nil {
				m := min(len(route), len(e.lastRoute[tf.Alias]))
				if len(route) != len(e.lastRoute[tf.Alias]) {
					e.routeChanges.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(m)).Inc()
				} else {
					// m - 1 because if the routes are the same apart from the destination, it's
					// just the destination that's changed, and that's recorded separately below
					for i := 0; i < (m - 1); i++ {
						if !reflect.DeepEqual(route[i], e.lastRoute[tf.Alias][i]) {
							e.routeChanges.WithLabelValues(tf.Alias, tf.Target, strconv.Itoa(i)).Inc()
						}
					}
				}
				e.lastRoute[tf.Alias] = route
			}
			if e.lastDest[tf.Alias] != nil && !reflect.DeepEqual(destination, e.lastDest[tf.Alias]) {
				e.destinationChanges.WithLabelValues(tf.Alias, tf.Target, e.lastDest[tf.Alias].String(), destination.String()).Inc()
				e.lastDest[tf.Alias] = destination
			}
		}
	}
}

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	e.sent.Collect(ch)
	e.received.Collect(ch)
	e.dropped.Collect(ch)
	e.lost.Collect(ch)
	e.latency.Collect(ch)
	e.routeChanges.Collect(ch)
	e.destinationChanges.Collect(ch)
	e.failed.Collect(ch)
	return
}

func trace(host Host, results chan<- *TargetFeedback) error {
	// run MTR and wait for it to complete
	a := mtr.New(1, host.Name, config.Arguments...)
	<-a.Done

	// output result
	if a.Error == nil {
		results <- &TargetFeedback{
			Target: host.Name,
			Alias:  host.Alias,
			Hosts:  a.Hosts,
		}
	}
	return a.Error
}

func worker(id int, host Host, results chan<- *TargetFeedback, wg *sync.WaitGroup) error {
	defer wg.Done()
	return trace(host, results)
}

func main() {
	var (
		configFile    = flag.String("config.file", "mtr.yaml", "MTR exporter configuration file.")
		listenAddress = flag.String("web.listen-address", ":9116", "The address to listen on for HTTP requests.")
		showVersion   = flag.Bool("version", false, "Print version information.")
	)

	flag.Parse()

	if *showVersion {
		fmt.Fprintln(os.Stdout, version.Print("mtr_exporter"))
		os.Exit(0)
	}

	log.Infoln("Starting mtr_exporter", version.Info())
	log.Infoln("Build context", version.BuildContext())

	yamlFile, err := ioutil.ReadFile(*configFile)

	if err != nil {
		log.Fatalf("Error reading config file: %s", err)
	}

	err = yaml.Unmarshal(yamlFile, &config)
	if err != nil {
		log.Fatalf("Error parsing config file: %s", err)
	}

	prometheus.MustRegister(version.NewCollector("mtr_exporter"))
	exporter := NewExporter()
	prometheus.MustRegister(exporter)

	go exporter.collect()

	http.Handle("/metrics", prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
            <title>MTR Exporter</title>

MTR Exporter

Metrics

            `))
	})

	log.Infoln("Listening on", *listenAddress)
	if err := http.ListenAndServe(*listenAddress, nil); err != nil {
		log.Fatalf("Error starting HTTP server: %s", err)
	}
}
<|endoftext|>


<commit_before>package glock

import (
	"sort"
	"sync"
	"time"
)

type (
	// MockClock is an implementation of Clock that can be moved forward in time
	// in increments for testing code that relies on timeouts or other time-sensitive
	// constructs.
	MockClock struct {
		fakeTime   time.Time
		triggers   mockTriggers
		tickers    mockTickers
		afterArgs  []time.Duration
		tickerArgs []time.Duration
		afterLock  sync.RWMutex
		tickerLock sync.Mutex
	}

	mockTrigger struct {
		trigger time.Time
		ch      chan time.Time
	}

	mockTriggers []*mockTrigger
	mockTickers  []*mockTicker
)

// NewMockClock creates a new MockClock with the internal time set
// to time.Now()
func NewMockClock() *MockClock {
	return NewMockClockAt(time.Now())
}

// NewMockClockAt creates a new MockClick with the internal time set
// to the provided time.
func NewMockClockAt(now time.Time) *MockClock {
	return &MockClock{
		fakeTime:   now,
		tickers:    make([]*mockTicker, 0),
		afterArgs:  make([]time.Duration, 0),
		tickerArgs: make([]time.Duration, 0),
	}
}

func (mc *MockClock) processTriggers() {
	mc.afterLock.Lock()
	defer mc.afterLock.Unlock()

	now := mc.Now()
	triggered := 0
	for _, trigger := range mc.triggers {
		if trigger.trigger.Before(now) || trigger.trigger.Equal(now) {
			trigger.ch <- trigger.trigger
			triggered++
		}
	}

	mc.triggers = mc.triggers[triggered:]
}

func (mc *MockClock) processTickers() {
	mc.tickerLock.Lock()
	defer mc.tickerLock.Unlock()

	now := mc.Now()
	for _, ticker := range mc.tickers {
		ticker.process(now)
	}
}

// SetCurrent sets the internal MockClock time to the supplied time.
func (mc *MockClock) SetCurrent(current time.Time) {
	mc.fakeTime = current
}

// Advance will advance the internal MockClock time by the supplied time.
func (mc *MockClock) Advance(duration time.Duration) {
	mc.fakeTime = mc.fakeTime.Add(duration)
	mc.processTriggers()
	mc.processTickers()
}

func (mc *MockClock) BlockingAdvance(duration time.Duration) {
	for {
		mc.afterLock.Lock()
		numTriggers := len(mc.triggers)
		mc.afterLock.Unlock()

		if numTriggers > 0 {
			break
		}
	}

	mc.Advance(duration)
}

// Now returns the current time internal to the MockClock
func (mc *MockClock) Now() time.Time {
	return mc.fakeTime
}

// After returns a channel that will be sent the current internal MockClock
// time once the MockClock's internal time is at or past the provided duration
func (mc *MockClock) After(duration time.Duration) <-chan time.Time {
	mc.afterLock.Lock()
	defer mc.afterLock.Unlock()

	trigger := &mockTrigger{
		trigger: mc.fakeTime.Add(duration),
		ch:      make(chan time.Time, 1),
	}

	mc.triggers = append(mc.triggers, trigger)
	sort.Sort(mc.triggers)
	mc.afterArgs = append(mc.afterArgs, duration)

	return trigger.ch
}

// BlockedOnAfter returns the number of calls to After that are blocked
// waiting for a call to Advance to trigger them.
func (mc *MockClock) BlockedOnAfter() int {
	mc.afterLock.RLock()
	defer mc.afterLock.RUnlock()

	return len(mc.triggers)
}

// Sleep will block until the internal MockClock time is at or past the
// provided duration
func (mc *MockClock) Sleep(duration time.Duration) {
	<-mc.After(duration)
}

// GetAfterArgs returns the duration of each call to After in the
// same order as they were called. The list is cleared each time
// GetAfterArgs is called.
func (mc *MockClock) GetAfterArgs() []time.Duration {
	mc.afterLock.Lock()
	defer mc.afterLock.Unlock()

	args := mc.afterArgs
	mc.afterArgs = mc.afterArgs[:0]
	return args
}

// GetTickerArgs returns the duration of each call to create a new
// ticker in the same order as they were called. The list is cleared
// each time GetTickerArgs is called.
func (mc *MockClock) GetTickerArgs() []time.Duration {
	mc.tickerLock.Lock()
	defer mc.tickerLock.Unlock()

	args := mc.tickerArgs
	mc.tickerArgs = mc.tickerArgs[:0]
	return args
}

type mockTicker struct {
	clock        *MockClock
	duration     time.Duration
	started      time.Time
	nextTick     time.Time
	processLock  sync.Mutex
	processQueue []time.Time
	writeLock    sync.Mutex
	writing      bool
	ch           chan time.Time
	stopped      bool
}

// NewTicker creates a new Ticker tied to the internal MockClock time that ticks
// at intervals similar to time.NewTicker().  It will also skip or drop ticks
// for slow readers similar to time.NewTicker() as well.
func (mc *MockClock) NewTicker(duration time.Duration) Ticker {
	if duration == 0 {
		panic("duration cannot be 0")
	}

	now := mc.Now()

	ft := &mockTicker{
		clock:        mc,
		duration:     duration,
		started:      now,
		nextTick:     now.Add(duration),
		processQueue: make([]time.Time, 0),
		ch:           make(chan time.Time),
	}

	mc.tickerLock.Lock()
	mc.tickers = append(mc.tickers, ft)
	mc.tickerArgs = append(mc.tickerArgs, duration)
	mc.tickerLock.Unlock()

	return ft
}

func (mt *mockTicker) process(now time.Time) {
	if mt.stopped {
		return
	}

	mt.processLock.Lock()
	mt.processQueue = append(mt.processQueue, now)
	mt.processLock.Unlock()

	if !mt.writing && (mt.nextTick.Before(now) || mt.nextTick.Equal(now)) {
		mt.writeLock.Lock()

		mt.writing = true
		go func() {
			defer mt.writeLock.Unlock()

			for {
				mt.processLock.Lock()
				if len(mt.processQueue) == 0 {
					mt.processLock.Unlock()
					break
				}

				procTime := mt.processQueue[0]
				mt.processQueue = mt.processQueue[1:]

				mt.processLock.Unlock()

				if mt.nextTick.After(procTime) {
					continue
				}

				mt.ch <- mt.nextTick

				durationMod := procTime.Sub(mt.started) % mt.duration

				if durationMod == 0 {
					mt.nextTick = procTime.Add(mt.duration)
				} else if procTime.Sub(mt.nextTick) > mt.duration {
					mt.nextTick = procTime.Add(mt.duration - durationMod)
				} else {
					mt.nextTick = mt.nextTick.Add(mt.duration)
				}
			}

			mt.writing = false
		}()
	}
}

// Chan returns a channel which will receive the MockClock's internal time
// at the interval given when creating the ticker.
func (mt *mockTicker) Chan() <-chan time.Time {
	return mt.ch
}

// Stop will stop the ticker from ticking
func (mt *mockTicker) Stop() {
	mt.stopped = true
}

//
// Trigger Sort

func (mt mockTriggers) Len() int {
	return len(mt)
}

func (mt mockTriggers) Less(i, j int) bool {
	return mt[i].trigger.Before(mt[j].trigger)
}

func (mt mockTriggers) Swap(i, j int) {
	mt[i], mt[j] = mt[j], mt[i]
}
<commit_msg>(Closes #6) Do not block the entire OS thread while looping in blocking advance.<commit_after>package glock

import (
	"runtime"
	"sort"
	"sync"
	"time"
)

type (
	// MockClock is an implementation of Clock that can be moved forward in time
	// in increments for testing code that relies on timeouts or other time-sensitive
	// constructs.
	MockClock struct {
		fakeTime   time.Time
		triggers   mockTriggers
		tickers    mockTickers
		afterArgs  []time.Duration
		tickerArgs []time.Duration
		afterLock  sync.RWMutex
		tickerLock sync.Mutex
	}

	mockTrigger struct {
		trigger time.Time
		ch      chan time.Time
	}

	mockTriggers []*mockTrigger
	mockTickers  []*mockTicker
)

// NewMockClock creates a new MockClock with the internal time set
// to time.Now()
func NewMockClock() *MockClock {
	return NewMockClockAt(time.Now())
}

// NewMockClockAt creates a new MockClick with the internal time set
// to the provided time.
func NewMockClockAt(now time.Time) *MockClock {
	return &MockClock{
		fakeTime:   now,
		tickers:    make([]*mockTicker, 0),
		afterArgs:  make([]time.Duration, 0),
		tickerArgs: make([]time.Duration, 0),
	}
}

func (mc *MockClock) processTriggers() {
	mc.afterLock.Lock()
	defer mc.afterLock.Unlock()

	now := mc.Now()
	triggered := 0
	for _, trigger := range mc.triggers {
		if trigger.trigger.Before(now) || trigger.trigger.Equal(now) {
			trigger.ch <- trigger.trigger
			triggered++
		}
	}

	mc.triggers = mc.triggers[triggered:]
}

func (mc *MockClock) processTickers() {
	mc.tickerLock.Lock()
	defer mc.tickerLock.Unlock()

	now := mc.Now()
	for _, ticker := range mc.tickers {
		ticker.process(now)
	}
}

// SetCurrent sets the internal MockClock time to the supplied time.
func (mc *MockClock) SetCurrent(current time.Time) {
	mc.fakeTime = current
}

// Advance will advance the internal MockClock time by the supplied time.
func (mc *MockClock) Advance(duration time.Duration) {
	mc.fakeTime = mc.fakeTime.Add(duration)
	mc.processTriggers()
	mc.processTickers()
}

// BlockingAdvance will call Advance but only after there is another routine
// which is blocking on the channel result of a call to After.
func (mc *MockClock) BlockingAdvance(duration time.Duration) {
	for mc.BlockedOnAfter() == 0 {
		runtime.Gosched()
	}

	mc.Advance(duration)
}

// Now returns the current time internal to the MockClock
func (mc *MockClock) Now() time.Time {
	return mc.fakeTime
}

// After returns a channel that will be sent the current internal MockClock
// time once the MockClock's internal time is at or past the provided duration
func (mc *MockClock) After(duration time.Duration) <-chan time.Time {
	mc.afterLock.Lock()
	defer mc.afterLock.Unlock()

	trigger := &mockTrigger{
		trigger: mc.fakeTime.Add(duration),
		ch:      make(chan time.Time, 1),
	}

	mc.triggers = append(mc.triggers, trigger)
	sort.Sort(mc.triggers)
	mc.afterArgs = append(mc.afterArgs, duration)

	return trigger.ch
}

// BlockedOnAfter returns the number of calls to After that are blocked
// waiting for a call to Advance to trigger them.
func (mc *MockClock) BlockedOnAfter() int {
	mc.afterLock.RLock()
	defer mc.afterLock.RUnlock()

	return len(mc.triggers)
}

// Sleep will block until the internal MockClock time is at or past the
// provided duration
func (mc *MockClock) Sleep(duration time.Duration) {
	<-mc.After(duration)
}

// GetAfterArgs returns the duration of each call to After in the
// same order as they were called. The list is cleared each time
// GetAfterArgs is called.
func (mc *MockClock) GetAfterArgs() []time.Duration {
	mc.afterLock.Lock()
	defer mc.afterLock.Unlock()

	args := mc.afterArgs
	mc.afterArgs = mc.afterArgs[:0]
	return args
}

// GetTickerArgs returns the duration of each call to create a new
// ticker in the same order as they were called. The list is cleared
// each time GetTickerArgs is called.
func (mc *MockClock) GetTickerArgs() []time.Duration {
	mc.tickerLock.Lock()
	defer mc.tickerLock.Unlock()

	args := mc.tickerArgs
	mc.tickerArgs = mc.tickerArgs[:0]
	return args
}

type mockTicker struct {
	clock        *MockClock
	duration     time.Duration
	started      time.Time
	nextTick     time.Time
	processLock  sync.Mutex
	processQueue []time.Time
	writeLock    sync.Mutex
	writing      bool
	ch           chan time.Time
	stopped      bool
}

// NewTicker creates a new Ticker tied to the internal MockClock time that ticks
// at intervals similar to time.NewTicker().  It will also skip or drop ticks
// for slow readers similar to time.NewTicker() as well.
func (mc *MockClock) NewTicker(duration time.Duration) Ticker {
	if duration == 0 {
		panic("duration cannot be 0")
	}

	now := mc.Now()

	ft := &mockTicker{
		clock:        mc,
		duration:     duration,
		started:      now,
		nextTick:     now.Add(duration),
		processQueue: make([]time.Time, 0),
		ch:           make(chan time.Time),
	}

	mc.tickerLock.Lock()
	mc.tickers = append(mc.tickers, ft)
	mc.tickerArgs = append(mc.tickerArgs, duration)
	mc.tickerLock.Unlock()

	return ft
}

func (mt *mockTicker) process(now time.Time) {
	if mt.stopped {
		return
	}

	mt.processLock.Lock()
	mt.processQueue = append(mt.processQueue, now)
	mt.processLock.Unlock()

	if !mt.writing && (mt.nextTick.Before(now) || mt.nextTick.Equal(now)) {
		mt.writeLock.Lock()

		mt.writing = true
		go func() {
			defer mt.writeLock.Unlock()

			for {
				mt.processLock.Lock()
				if len(mt.processQueue) == 0 {
					mt.processLock.Unlock()
					break
				}

				procTime := mt.processQueue[0]
				mt.processQueue = mt.processQueue[1:]

				mt.processLock.Unlock()

				if mt.nextTick.After(procTime) {
					continue
				}

				mt.ch <- mt.nextTick

				durationMod := procTime.Sub(mt.started) % mt.duration

				if durationMod == 0 {
					mt.nextTick = procTime.Add(mt.duration)
				} else if procTime.Sub(mt.nextTick) > mt.duration {
					mt.nextTick = procTime.Add(mt.duration - durationMod)
				} else {
					mt.nextTick = mt.nextTick.Add(mt.duration)
				}
			}

			mt.writing = false
		}()
	}
}

// Chan returns a channel which will receive the MockClock's internal time
// at the interval given when creating the ticker.
func (mt *mockTicker) Chan() <-chan time.Time {
	return mt.ch
}

// Stop will stop the ticker from ticking
func (mt *mockTicker) Stop() {
	mt.stopped = true
}

//
// Trigger Sort

func (mt mockTriggers) Len() int {
	return len(mt)
}

func (mt mockTriggers) Less(i, j int) bool {
	return mt[i].trigger.Before(mt[j].trigger)
}

func (mt mockTriggers) Swap(i, j int) {
	mt[i], mt[j] = mt[j], mt[i]
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
)

func Error(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

var initial_environ, current_environ map[string]string

const env_prefix = "HTTPCLI_"
const header_prefix = env_prefix + "HEADER_"

func headerkey_to_envkey(k string) string {
	return header_prefix + strings.Replace(k, "-", "_", -1)
}

func envkey_to_headerkey(k string) string {
	if !strings.HasPrefix(k, header_prefix) {
		return ""
	}

	return strings.Replace(k[len(header_prefix):], "_", "-", -1)
}

// Convert a []string environment from os.Environ() to a more usable map.
func environ_to_map(environ []string) (m map[string]string) {
	m = make(map[string]string)
	for _, kv := range environ {
		// Ignore things we don't care about:
		if !strings.HasPrefix(kv, env_prefix) {
			continue
		}
		// Split by first '=':
		equ := strings.IndexByte(kv, '=')
		if equ == -1 {
			continue
		}
		name, value := kv[:equ], kv[equ+1:]
		m[name] = value
	}
	return
}

func setenv(key, value string) (err error) {
	err = os.Setenv(key, value)
	if err != nil {
		return err
	}

	// Replicate the setting in the current_environ map:
	if value == "" {
		delete(current_environ, key)
		return nil
	}

	current_environ[key] = value
	return nil
}

// Get HTTP headers from environment:
func get_headers() (headers http.Header) {
	headers = make(http.Header)
	for key, value := range current_environ {
		if !strings.HasPrefix(key, header_prefix) {
			continue
		}
		name := envkey_to_headerkey(key)
		headers.Set(name, value)
	}
	return
}

// Set HTTP headers back to environment:
func set_headers(headers http.Header) {
	// Unset removed headers first:
	for key, _ := range current_environ {
		if !strings.HasPrefix(key, header_prefix) {
			continue
		}
		name := envkey_to_headerkey(key)
		if _, ok := headers[name]; !ok {
			setenv(key, "")
		}
	}

	// Set new headers:
	for key, values := range headers {
		name := headerkey_to_envkey(key)
		value := strings.Join(values, " ")
		err := setenv(name, value)
		if err != nil {
			Error("Error setting $%s: %s\n", name, err)
			os.Exit(2)
			return
		}
	}
}

func get_abs_url() *url.URL {
	url_s := current_environ[env_prefix+"URL"]
	if url_s == "" {
		Error("Missing $%sURL env var\n", env_prefix)
		os.Exit(2)
		return nil
	}
	base_url, err := url.Parse(url_s)
	if err != nil {
		Error("Error parsing $%sURL: %s\n", env_prefix, err)
		os.Exit(2)
		return nil
	}
	if !base_url.IsAbs() {
		Error("$%sURL must be an absolute URL\n", env_prefix)
		os.Exit(2)
		return nil
	}
	return base_url
}

func set_abs_url(base_url *url.URL) {
	err := setenv(env_prefix+"URL", base_url.String())
	if err != nil {
		Error("Error setting $%sURL: %s\n", env_prefix, err)
		os.Exit(2)
		return
	}
}

// Output a bash script to modify the environment:
func output_bash_env() {
	// Unset removed headers first:
	for key, _ := range initial_environ {
		if _, ok := current_environ[key]; !ok {
			fmt.Printf("unset %s\n", key)
		}
	}

	// Set new or existing headers:
	for key, value := range current_environ {
		if initial_environ[key] == value {
			continue
		}

		// Bash-escape the value as a single-quoted string with escaping rules:
		bash_escape_value := strings.Replace(value, "\\", "\\\\", -1)
		bash_escape_value = strings.Replace(bash_escape_value, "'", "\\'", -1)

		fmt.Printf("export %s=$'%s'\n", key, bash_escape_value)
	}

	// Comments cannot come first if using `eval`.
	// fmt.Println()
	// fmt.Println("# This output must be `eval`ed in bash in order to have effect on the current environment.")
	// fmt.Println("# Example: $ eval `http-cli ...`")
}

func do_http(http_method string, body_required bool, args []string) {
	// Get environment:
	base_url := get_abs_url()
	headers := get_headers()

	if len(args) == 0 {
		Error("Missing required relative URL\n")
		os.Exit(1)
		return
	}

	// Parse relative URL:
	rel_url_s := args[0]
	rel_url, err := url.Parse(rel_url_s)
	if err != nil {
		Error("Error parsing relative URL: %s\n", err)
		os.Exit(1)
		return
	}

	// Combine absolute URL base with relative URL argument:
	api_url := &url.URL{
		Scheme:   base_url.Scheme,
		Host:     base_url.Host,
		User:     base_url.User,
		Path:     path.Join(base_url.Path, rel_url.Path),
		RawQuery: rel_url.RawQuery,
		Fragment: rel_url.Fragment,
	}

	// Set up the request:
	req := &http.Request{
		URL:    api_url,
		Method: http_method,
		Header: headers,
	}

	// Exclude named headers:
	// TODO: parse args for this!
	exclude_headers_arg := ""
	if exclude_headers_arg != "" {
		// Remove excluded headers:
		exclude_headers := strings.Split(exclude_headers_arg, ",")
		for _, name := range exclude_headers {
			delete(req.Header, name)
		}
	}

	// Set up body content-type and data:
	var body_data []byte
	if body_required {
		// Read all of stdin to a `[]byte` buffer:
		body_data, err = ioutil.ReadAll(os.Stdin)
		if err != nil {
			Error("Error reading stdin: %s", err)
			os.Exit(3)
			return
		}

		if len(args) >= 2 {
			req.Header.Set("Content-Type", args[1])
		}

		// Create a Buffer to read the `[]byte` as the HTTP body:
		buf := bytes.NewBuffer(body_data)
		req.Body = ioutil.NopCloser(buf)
		req.ContentLength = int64(buf.Len())
	}

	// for debugging:
	Error("%s %s\n", http_method, api_url)
	for key, values := range req.Header {
		Error("%s: %s\n", key, strings.Join(values, " "))
	}
	Error("\n")
	if body_required {
		Error("%s\n", body_data)
	}

	// Make the request:
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		Error("HTTP error: %s\n", err)
		os.Exit(3)
		return
	}

	if resp.StatusCode != 200 {
		Error("%d\n", resp.StatusCode)
	}
	if resp.Body != nil {
		_, err = io.Copy(os.Stdout, resp.Body)
		if err != nil {
			Error("Error copying: %s\n", err)
		}
		// For nicer shell output in the event that stderr -> stdout.
		// We don't want to append any unnecessary \n to stdout.
		fmt.Fprintln(os.Stderr)
	}
}

func main() {
	args := os.Args[1:][:]
	if len(args) == 0 {
		Error(`Usage:
http-cli <command> [args...]

Commands:
  url    [absolute_url]
    Get or set base URL in environment.

  clear
    Clears all HTTP headers in environment.

  set <header_name> <header_value>
    Sets a custom HTTP header in environment.

  list
    List current HTTP headers in environment.

  env
    Generate a bash script to export current environment.

  GET    <relative-url>
  DELETE <relative-url>
    Invoke HTTP GET or DELETE. No body data is sent.

  POST   <relative_url> [content-type]
  PUT    <relative_url> [content-type]
    Invoke HTTP POST or PUT. Body data is read from stdin.
`)
		os.Exit(1)
		return
	}

	// Determine what to do:
	cmd := args[0]
	args = args[1:]

	// Copy current environment:
	initial_environ = environ_to_map(os.Environ())
	current_environ = environ_to_map(os.Environ())

	body_required := true
	switch strings.ToLower(cmd) {
	case "get", "delete":
		body_required = false
		fallthrough
	case "post", "put":
		do_http(strings.ToUpper(cmd), body_required, args)
		break

	case "url":
		// Must be evaluated on the bash console as "eval `http-cli header-set ...`"
		if len(args) == 0 {
			base_url := get_abs_url()
			fmt.Printf("%s\n", base_url)
		} else if len(args) == 1 {
			base_url, err := url.Parse(args[0])
			if err != nil {
				Error("Error parsing absolute URL: %s\n", err)
				os.Exit(1)
				return
			}
			set_abs_url(base_url)
			output_bash_env()
		}
		break

	case "list":
		// Get HTTP headers from environment:
		headers := get_headers()
		for key, values := range headers {
			fmt.Printf("%s: %s\n", key, strings.Join(values, " "))
		}
		break

	case "set":
		// Must be evaluated on the bash console as "eval `http-cli header-set ...`"

		// Get HTTP headers from environment:
		headers := get_headers()
		if len(args) == 2 {
			// Set a new HTTP header:
			headers.Set(args[0], args[1])
		} else if len(args) == 1 {
			delete(headers, args[0])
		} else {

		}

		set_headers(headers)

		// Output the bash evaluation statements:
		output_bash_env()
		break

	case "clear":
		// Must be evaluated on the bash console as "eval `http-cli header-clear ...`"
		set_headers(nil)

		// Output the bash evaluation statements:
		output_bash_env()
		break

	case "env":
		// Output the bash evaluation statements:
		initial_environ = make(map[string]string)
		output_bash_env()
		break

	default:
		os.Exit(1)
		break
	}

}
<commit_msg>Output binary name in help.<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
)

func Error(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

var initial_environ, current_environ map[string]string

const env_prefix = "HTTPCLI_"
const header_prefix = env_prefix + "HEADER_"

func headerkey_to_envkey(k string) string {
	return header_prefix + strings.Replace(k, "-", "_", -1)
}

func envkey_to_headerkey(k string) string {
	if !strings.HasPrefix(k, header_prefix) {
		return ""
	}

	return strings.Replace(k[len(header_prefix):], "_", "-", -1)
}

// Convert a []string environment from os.Environ() to a more usable map.
func environ_to_map(environ []string) (m map[string]string) {
	m = make(map[string]string)
	for _, kv := range environ {
		// Ignore things we don't care about:
		if !strings.HasPrefix(kv, env_prefix) {
			continue
		}
		// Split by first '=':
		equ := strings.IndexByte(kv, '=')
		if equ == -1 {
			continue
		}
		name, value := kv[:equ], kv[equ+1:]
		m[name] = value
	}
	return
}

func setenv(key, value string) (err error) {
	err = os.Setenv(key, value)
	if err != nil {
		return err
	}

	// Replicate the setting in the current_environ map:
	if value == "" {
		delete(current_environ, key)
		return nil
	}

	current_environ[key] = value
	return nil
}

// Get HTTP headers from environment:
func get_headers() (headers http.Header) {
	headers = make(http.Header)
	for key, value := range current_environ {
		if !strings.HasPrefix(key, header_prefix) {
			continue
		}
		name := envkey_to_headerkey(key)
		headers.Set(name, value)
	}
	return
}

// Set HTTP headers back to environment:
func set_headers(headers http.Header) {
	// Unset removed headers first:
	for key, _ := range current_environ {
		if !strings.HasPrefix(key, header_prefix) {
			continue
		}
		name := envkey_to_headerkey(key)
		if _, ok := headers[name]; !ok {
			setenv(key, "")
		}
	}

	// Set new headers:
	for key, values := range headers {
		name := headerkey_to_envkey(key)
		value := strings.Join(values, " ")
		err := setenv(name, value)
		if err != nil {
			Error("Error setting $%s: %s\n", name, err)
			os.Exit(2)
			return
		}
	}
}

func get_abs_url() *url.URL {
	url_s := current_environ[env_prefix+"URL"]
	if url_s == "" {
		Error("Missing $%sURL env var\n", env_prefix)
		os.Exit(2)
		return nil
	}
	base_url, err := url.Parse(url_s)
	if err != nil {
		Error("Error parsing $%sURL: %s\n", env_prefix, err)
		os.Exit(2)
		return nil
	}
	if !base_url.IsAbs() {
		Error("$%sURL must be an absolute URL\n", env_prefix)
		os.Exit(2)
		return nil
	}
	return base_url
}

func set_abs_url(base_url *url.URL) {
	err := setenv(env_prefix+"URL", base_url.String())
	if err != nil {
		Error("Error setting $%sURL: %s\n", env_prefix, err)
		os.Exit(2)
		return
	}
}

// Output a bash script to modify the environment:
func output_bash_env() {
	// Unset removed headers first:
	for key, _ := range initial_environ {
		if _, ok := current_environ[key]; !ok {
			fmt.Printf("unset %s\n", key)
		}
	}

	// Set new or existing headers:
	for key, value := range current_environ {
		if initial_environ[key] == value {
			continue
		}

		// Bash-escape the value as a single-quoted string with escaping rules:
		bash_escape_value := strings.Replace(value, "\\", "\\\\", -1)
		bash_escape_value = strings.Replace(bash_escape_value, "'", "\\'", -1)

		fmt.Printf("export %s=$'%s'\n", key, bash_escape_value)
	}

	// Comments cannot come first if using `eval`.
	// fmt.Println()
	// fmt.Println("# This output must be `eval`ed in bash in order to have effect on the current environment.")
	// fmt.Println("# Example: $ eval `http ...`")
}

func do_http(http_method string, body_required bool, args []string) {
	// Get environment:
	base_url := get_abs_url()
	headers := get_headers()

	if len(args) == 0 {
		Error("Missing required relative URL\n")
		os.Exit(1)
		return
	}

	// Parse relative URL:
	rel_url_s := args[0]
	rel_url, err := url.Parse(rel_url_s)
	if err != nil {
		Error("Error parsing relative URL: %s\n", err)
		os.Exit(1)
		return
	}

	// Combine absolute URL base with relative URL argument:
	api_url := &url.URL{
		Scheme:   base_url.Scheme,
		Host:     base_url.Host,
		User:     base_url.User,
		Path:     path.Join(base_url.Path, rel_url.Path),
		RawQuery: rel_url.RawQuery,
		Fragment: rel_url.Fragment,
	}

	// Set up the request:
	req := &http.Request{
		URL:    api_url,
		Method: http_method,
		Header: headers,
	}

	// Exclude named headers:
	// TODO: parse args for this!
	exclude_headers_arg := ""
	if exclude_headers_arg != "" {
		// Remove excluded headers:
		exclude_headers := strings.Split(exclude_headers_arg, ",")
		for _, name := range exclude_headers {
			delete(req.Header, name)
		}
	}

	// Set up body content-type and data:
	var body_data []byte
	if body_required {
		// Read all of stdin to a `[]byte` buffer:
		body_data, err = ioutil.ReadAll(os.Stdin)
		if err != nil {
			Error("Error reading stdin: %s", err)
			os.Exit(3)
			return
		}

		if len(args) >= 2 {
			req.Header.Set("Content-Type", args[1])
		}

		// Create a Buffer to read the `[]byte` as the HTTP body:
		buf := bytes.NewBuffer(body_data)
		req.Body = ioutil.NopCloser(buf)
		req.ContentLength = int64(buf.Len())
	}

	// for debugging:
	Error("%s %s\n", http_method, api_url)
	for key, values := range req.Header {
		Error("%s: %s\n", key, strings.Join(values, " "))
	}
	Error("\n")
	if body_required {
		Error("%s\n", body_data)
	}

	// Make the request:
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		Error("HTTP error: %s\n", err)
		os.Exit(3)
		return
	}

	if resp.StatusCode != 200 {
		Error("%d\n", resp.StatusCode)
	}
	if resp.Body != nil {
		_, err = io.Copy(os.Stdout, resp.Body)
		if err != nil {
			Error("Error copying: %s\n", err)
		}
		// For nicer shell output in the event that stderr -> stdout.
		// We don't want to append any unnecessary \n to stdout.
		fmt.Fprintln(os.Stderr)
	}
}

func main() {
	args := os.Args[1:][:]
	if len(args) == 0 {
		Error(`Usage:
%s <command> [args...]

Commands:
  url    [absolute_url]
    Get or set base URL in environment.

  clear
    Clears all HTTP headers in environment.

  set <header_name> <header_value>
    Sets a custom HTTP header in environment.

  list
    List current HTTP headers in environment.

  env
    Generate a bash script to export current environment.

  GET    <relative-url>
  DELETE <relative-url>
    Invoke HTTP GET or DELETE. No body data is sent.

  POST   <relative_url> [content-type]
  PUT    <relative_url> [content-type]
    Invoke HTTP POST or PUT. Body data is read from stdin.
`, os.Args[0])
		os.Exit(1)
		return
	}

	// Determine what to do:
	cmd := args[0]
	args = args[1:]

	// Copy current environment:
	initial_environ = environ_to_map(os.Environ())
	current_environ = environ_to_map(os.Environ())

	body_required := true
	switch strings.ToLower(cmd) {
	case "get", "delete":
		body_required = false
		fallthrough
	case "post", "put":
		do_http(strings.ToUpper(cmd), body_required, args)
		break

	case "url":
		// Must be evaluated on the bash console as "eval `http header-set ...`"
		if len(args) == 0 {
			base_url := get_abs_url()
			fmt.Printf("%s", base_url)
			fmt.Fprintln(os.Stderr)
		} else if len(args) == 1 {
			base_url, err := url.Parse(args[0])
			if err != nil {
				Error("Error parsing absolute URL: %s\n", err)
				os.Exit(1)
				return
			}
			set_abs_url(base_url)
			output_bash_env()
		}
		break

	case "list":
		// Get HTTP headers from environment:
		headers := get_headers()
		for key, values := range headers {
			fmt.Printf("%s: %s\n", key, strings.Join(values, " "))
		}
		break

	case "set":
		// Must be evaluated on the bash console as "eval `http header-set ...`"

		// Get HTTP headers from environment:
		headers := get_headers()
		if len(args) == 2 {
			// Set a new HTTP header:
			headers.Set(args[0], args[1])
		} else if len(args) == 1 {
			delete(headers, args[0])
		} else {

		}

		set_headers(headers)

		// Output the bash evaluation statements:
		output_bash_env()
		break

	case "clear":
		// Must be evaluated on the bash console as "eval `http header-clear ...`"
		set_headers(nil)

		// Output the bash evaluation statements:
		output_bash_env()
		break

	case "env":
		// Output the bash evaluation statements:
		initial_environ = make(map[string]string)
		output_bash_env()
		break

	default:
		os.Exit(1)
		break
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jesselucas/slackcmd/slack"
	"github.com/jesselucas/slackcmd/commands/beats1"
	"github.com/jesselucas/slackcmd/commands/calendar"
	"github.com/jesselucas/slackcmd/commands/trello"
	"github.com/jesselucas/slackcmd/slack"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
)

// struct used to store environment variables from config.json
type env struct {
	Key   string
	Value string
}

func setEnvFromJSON(configPath string) {
	configFile, err := ioutil.ReadFile(configPath)
	if err != nil {
		fmt.Println("config.json not found. Using os environment variables.")
		return
	}

	var envVars []env
	json.Unmarshal(configFile, &envVars)

	// set environment variables
	for _, env := range envVars {
		// fmt.Println(env)
		os.Setenv(env.Key, env.Value)
	}

}

func createSlashCommand(w http.ResponseWriter, r *http.Request) *slack.SlashCommand {
	var v url.Values

	switch r.Method {
	case "POST":
		r.ParseForm()
		v = r.Form
	case "GET":
		v = r.URL.Query()
	}

	sc := &slack.SlashCommand{
		Token:       v.Get("token"),
		TeamId:      v.Get("team_id"),
		TeamDomain:  v.Get("team_domain"),
		ChannelId:   v.Get("channel_id"),
		ChannelName: v.Get("channel_name"),
		UserId:      v.Get("user_id"),
		UserName:    v.Get("user_name"),
		Command:     v.Get("command"),
		Text:        v.Get("text"),
		Hook:        v.Get("hook"),
	}

	return sc
}

func commandHandler(w http.ResponseWriter, r *http.Request) {
	sc := createSlashCommand(w, r)

	// interface reference for slack Commands
	var cmd slack.Command

	// Add commands here
	switch sc.Command {
	case "/fg":
		cmd = trello.Command{}
	case "/beats1":
		cmd = beats1.Command{}
	case "/conference":
		cmd = calendar.Command{}
	default:
		err := errors.New("No Command found")
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

	fmt.Println("slash command:", sc.Text)

	// command request returns payload
	cp, err := cmd.Request(sc)

	if cp == nil {
		err := errors.New("Unauthorized")
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

	if err != nil {
		err := errors.New("Unauthorized")
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	} else {

		// check if the command wants to send a slash command response
		if cp.SlashResponse {
			w.Write([]byte(cp.Text))
		}

		// don't send payload if hook URL isn't passed
		if sc.Hook != "" && cp.SendPayload == true {
			cpJSON, err := json.Marshal(cp)
			if err != nil {
				err := errors.New("Unauthorized")
				http.Error(w, err.Error(), http.StatusForbidden)
				return
			}

			cpJSONString := string(cpJSON[:])

			// Make the request to the Slack API.
			http.PostForm(sc.Hook, url.Values{"payload": {cpJSONString}})
		}
	}
}

func main() {
	// setup environment variables if a config json exist
	setEnvFromJSON("config.json")

	// url setup. FIX make more generic
	var url string
	if os.Getenv("PORT") != "" {
		url = ":" + os.Getenv("PORT")
	} else {
		url = "localhost:8080"
	}

	// vs := validateSlackToken(http.HandlerFunc(commandHandler), slackAPIKey)
	http.HandleFunc("/cmd/", commandHandler)
	http.HandleFunc("/cmd", commandHandler)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Go away!")
	})
	log.Fatal(http.ListenAndServe(url, nil))
}
<commit_msg>Removing duplicate import.<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jesselucas/slackcmd/slack"
	"github.com/jesselucas/slackcmd/commands/beats1"
	"github.com/jesselucas/slackcmd/commands/calendar"
	"github.com/jesselucas/slackcmd/commands/trello"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
)

// struct used to store environment variables from config.json
type env struct {
	Key   string
	Value string
}

func setEnvFromJSON(configPath string) {
	configFile, err := ioutil.ReadFile(configPath)
	if err != nil {
		fmt.Println("config.json not found. Using os environment variables.")
		return
	}

	var envVars []env
	json.Unmarshal(configFile, &envVars)

	// set environment variables
	for _, env := range envVars {
		// fmt.Println(env)
		os.Setenv(env.Key, env.Value)
	}

}

func createSlashCommand(w http.ResponseWriter, r *http.Request) *slack.SlashCommand {
	var v url.Values

	switch r.Method {
	case "POST":
		r.ParseForm()
		v = r.Form
	case "GET":
		v = r.URL.Query()
	}

	sc := &slack.SlashCommand{
		Token:       v.Get("token"),
		TeamId:      v.Get("team_id"),
		TeamDomain:  v.Get("team_domain"),
		ChannelId:   v.Get("channel_id"),
		ChannelName: v.Get("channel_name"),
		UserId:      v.Get("user_id"),
		UserName:    v.Get("user_name"),
		Command:     v.Get("command"),
		Text:        v.Get("text"),
		Hook:        v.Get("hook"),
	}

	return sc
}

func commandHandler(w http.ResponseWriter, r *http.Request) {
	sc := createSlashCommand(w, r)

	// interface reference for slack Commands
	var cmd slack.Command

	// Add commands here
	switch sc.Command {
	case "/fg":
		cmd = trello.Command{}
	case "/beats1":
		cmd = beats1.Command{}
	case "/conference":
		cmd = calendar.Command{}
	default:
		err := errors.New("No Command found")
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

	fmt.Println("slash command:", sc.Text)

	// command request returns payload
	cp, err := cmd.Request(sc)

	if cp == nil {
		err := errors.New("Unauthorized")
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

	if err != nil {
		err := errors.New("Unauthorized")
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	} else {

		// check if the command wants to send a slash command response
		if cp.SlashResponse {
			w.Write([]byte(cp.Text))
		}

		// don't send payload if hook URL isn't passed
		if sc.Hook != "" && cp.SendPayload == true {
			cpJSON, err := json.Marshal(cp)
			if err != nil {
				err := errors.New("Unauthorized")
				http.Error(w, err.Error(), http.StatusForbidden)
				return
			}

			cpJSONString := string(cpJSON[:])

			// Make the request to the Slack API.
			http.PostForm(sc.Hook, url.Values{"payload": {cpJSONString}})
		}
	}
}

func main() {
	// setup environment variables if a config json exist
	setEnvFromJSON("config.json")

	// url setup. FIX make more generic
	var url string
	if os.Getenv("PORT") != "" {
		url = ":" + os.Getenv("PORT")
	} else {
		url = "localhost:8080"
	}

	// vs := validateSlackToken(http.HandlerFunc(commandHandler), slackAPIKey)
	http.HandleFunc("/cmd/", commandHandler)
	http.HandleFunc("/cmd", commandHandler)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Go away!")
	})
	log.Fatal(http.ListenAndServe(url, nil))
}
<|endoftext|>