<commit_before>/*
	Watches the current directory for changes, and runs the command you supply as arguments
	in response to changes.
*/

/*
	TODO: catch ctrl-c and ensure clean output so term isn't screwed
	this can happen from killing commands like erlang's iex
	see https://askubuntu.com/questions/171449/shell-does-not-show-typed-in-commands-reset-works-but-what-happened

	TODO: specifying watch regex w/o cmd errors
*/

package main

import (
	"flag"
	"fmt"
	"github.com/JonahBraun/dog"
	"github.com/howeyc/fsnotify"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"sync"
)

var (
	log     = dog.NewDog(dog.DEBUG)
	verbose = flag.Bool("v", false, "Verbose")
	quiet   = flag.Bool("q", false, "Quiet, only warnings and errors")

	buildCmd      = flag.String("cmd", "", "Bash command to run on change, Wabo will wait for this command to finish")
	daemonCmd     = flag.String("daemon", "", "Bash command that starts a daemon, Wago will halt if the daemon exits before the trigger or timer")
	daemonTimer   = flag.Int("timer", 0, "Miliseconds to wait after starting daemon before continuing")
	daemonTrigger = flag.String("trigger", "", "A string the daemon will output that indicates it has started successfuly, Wago will continue on this trigger")
	exitWait      = flag.Int("exitwait", 0, "If 0, kills processes immediately, if >0, sends SIGINT and waits X ms for process to exit before killing")
	fiddle        = flag.Bool("fiddle", false, "CLI fiddle mode, starts a web server and opens url to targetDir/index.html")
	leader        = flag.String("leader", "", "Leader character for wago output (to differentiate from command output), defaults to emoji")
	postCmd       = flag.String("pcmd", "", "Bash command to run after the daemon has successfully started, use this to kick off your test suite")
	recursive     = flag.Bool("recursive", true, "Watch directory tree recursively")
	targetDir     = flag.String("dir", "", "Directory to watch, defaults to current")
	url           = flag.String("url", "", "URL to open")
	watchRegex    = flag.String("watch", `/\w[\w\.]*": (CREATE|MODIFY)`, "Regex to match watch event, use -v to see all events")
	webServer     = flag.String("web", "", "Start a web server at this address, e.g. :8420")
	shell         = flag.String("shell", "", "Shell used to run commands, defaults to $SHELL, fallback to /bin/sh")
)

type Watcher struct {
	Event chan fmt.Stringer
	Error chan error
}

func main() {
	// the following function calls merely serve to logically organize what
	// is otherwise a VERY lengthy setup

	// TODO: have configSetup return a config object so that the reliance on
	// config globals is removed
	configSetup()

	startWebServer()

	runChain(newWatcher(), make(chan struct{}))
}

func runChain(watcher *Watcher, quit chan struct{}) {
	chain := make([]Runnable, 0, 5)

	// build chain of runnables
	if len(*buildCmd) > 0 {
		chain = append(chain, NewRunWait(*buildCmd))
	}
	if len(*daemonCmd) > 0 {
		if len(*daemonTrigger) > 0 {
			chain = append(chain, NewDaemonTrigger(*daemonCmd, *daemonTrigger))
		} else {
			chain = append(chain, NewDaemonTimer(*daemonCmd, *daemonTimer))
		}
	}
	if len(*postCmd) > 0 {
		chain = append(chain, NewRunWait(*postCmd))
	}
	if *url != "" {
		chain = append(chain, NewBrowser(*url))
	}

	eventRegex, err := regexp.Compile(*watchRegex)
	if err != nil {
		log.Fatal("Watch regex compile error:", err)(1)
	}

	var wg sync.WaitGroup

	// main loop
	for {
		// all channels of struct{} are disposable, single use
		// kill is passed to all Runnable so they know when they should exit
		kill := make(chan struct{})
		// abort is to single the event loop that a Runnable did not succeed so cancel everything
		abort := make(chan struct{})

		var drain func()
		drain = func() {
			select {
			case ev := <-watcher.Event:
				log.Debug("Extra event ignored:", ev.String())
				drain()
			default:
			}
		}
		drain()

		// event loop
		go func() {
			for {
				select {
				case ev := <-watcher.Event:
					if eventRegex.MatchString(ev.String()) {
						log.Info("Matched event:", ev.String())
						close(kill)
						return
					} else {
						log.Debug("Ignored event:", ev.String())
					}
				case err = <-watcher.Error:
					log.Fatal("Watcher error:", err)(5)
				case <-abort:
					close(kill)
					return
				case <-quit:
					// currently only used by test suite
					close(kill)
					return
				}
			}
		}()

	RunLoop:
		for _, runnable := range chain {
			done, dead := runnable(kill)
			wg.Add(1)

			go func() {
				wg.Done()
				<-dead
			}()

			select {
			case d := <-done:
				if !d {
					// Runnable failed it's success metric
					close(abort)
					break RunLoop
				}
			case <-kill:
				break RunLoop
			}
		}

		// ensure an event has occured, we may be here because all runnables completed
		<-kill

		// ensure all runnables (procs) are dead before restarting the chain
		wg.Wait()

		// check if we should quit, currently only used by test suites for teardown
		select {
		case <-quit:
			log.Warn("Quitting run chain")
			return
		default:
		}
	}
}

func newWatcher() *Watcher {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}

	watchDir := func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			return nil
		}

		log.Debug("Watching dir:", path)

		if err != nil {
			log.Err("Skipping dir:", path, err)
			return filepath.SkipDir
		}

		err = watcher.Watch(path)
		if err != nil {
			panic(err)
		}

		return nil
	}

	if *recursive == true {
		err = filepath.Walk(*targetDir, watchDir)
		if err != nil {
			panic(err)
		}
	} else {
		err = watcher.Watch(*targetDir)
		if err != nil {
			panic(err)
		}
	}

	// To facilitate testing (which sends artifical events from a timer),
	// we have an abstracted struct Watcher that holds the applicable channels.
	// fsnotify.FileEvent is a fmt.Stringer, but channels cannot be converted.
	// Unfortunately, an extra channel is necessary to perform the conversion.
	event := make(chan fmt.Stringer)
	go func() {
		for {
			event <- <-watcher.Event
		}
	}()

	return &Watcher{event, watcher.Error}
}

func startWebServer() {
	if *webServer != "" {
		go func() {
			log.Info("Starting web server on port", *webServer)
			err := http.ListenAndServe(*webServer, http.FileServer(http.Dir(*targetDir)))
			if err != nil {
				log.Fatal("Error starting web server:", err)(2)
			}
		}()
	}
}

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

	flag.Usage = func() {
		fmt.Println("WaGo (Watch, Go) build tool. Usage:")
		flag.PrintDefaults()
	}

	// TODO: this should check for actions
	if len(os.Args) < 2 {
		flag.Usage()
		log.Fatal("You must specify an action")(1)
	}

	flag.Parse()

	if *verbose {
		log = dog.NewDog(dog.DEBUG)
	} else if *quiet {
		log = dog.NewDog(dog.WARN)
	} else {
		log = dog.NewDog(dog.INFO)
	}

	if len(*shell) == 0 {
		*shell = os.Getenv("SHELL")
		if len(*shell) == 0 {
			*shell = "/bin/sh"
		}
	}
	log.Debug("Using shell", *shell)

	if (len(*daemonTrigger) > 0) && (*daemonTimer > 0) {
		log.Fatal("Both daemon trigger and timer specified, use only one")(1)
	}

	if (len(*daemonTrigger) > 0 || *daemonTimer > 0) && len(*daemonCmd) == 0 {
		log.Fatal("Specify a daemon command to use the trigger or timer")(1)
	}

	if len(*buildCmd) == 0 && len(*daemonCmd) == 0 && !*fiddle && len(*postCmd) == 0 && len(*url) == 0 && len(*webServer) == 0 {
		flag.Usage()
		log.Fatal("You must specify an action")(1)
	}

	if *fiddle {
		if *webServer == "" {
			*webServer = ":9933"
		}
		if *url == "" {
			*url = "http://localhost" + *webServer + "/"
		}
	}

	if *targetDir == "" {
		cwd, err := os.Getwd()
		if err != nil {
			panic(err)
		}
		targetDir = &cwd
	}
	log.Debug("Target dir:", *targetDir)
}
<commit_msg>update comment<commit_after>// Wago (Watch, Go)
// A general purpose watch / build development tool.

// TODO: catch SIGINT and send dog.TR to ensure a clean term
// see https://askubuntu.com/questions/171449/shell-does-not-show-typed-in-commands-reset-works-but-what-happened

package main

import (
	"flag"
	"fmt"
	"github.com/JonahBraun/dog"
	"github.com/howeyc/fsnotify"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"sync"
)

var (
	log     = dog.NewDog(dog.DEBUG)
	verbose = flag.Bool("v", false, "Verbose")
	quiet   = flag.Bool("q", false, "Quiet, only warnings and errors")

	buildCmd      = flag.String("cmd", "", "Bash command to run on change, Wabo will wait for this command to finish")
	daemonCmd     = flag.String("daemon", "", "Bash command that starts a daemon, Wago will halt if the daemon exits before the trigger or timer")
	daemonTimer   = flag.Int("timer", 0, "Miliseconds to wait after starting daemon before continuing")
	daemonTrigger = flag.String("trigger", "", "A string the daemon will output that indicates it has started successfuly, Wago will continue on this trigger")
	exitWait      = flag.Int("exitwait", 0, "If 0, kills processes immediately, if >0, sends SIGINT and waits X ms for process to exit before killing")
	fiddle        = flag.Bool("fiddle", false, "CLI fiddle mode, starts a web server and opens url to targetDir/index.html")
	leader        = flag.String("leader", "", "Leader character for wago output (to differentiate from command output), defaults to emoji")
	postCmd       = flag.String("pcmd", "", "Bash command to run after the daemon has successfully started, use this to kick off your test suite")
	recursive     = flag.Bool("recursive", true, "Watch directory tree recursively")
	targetDir     = flag.String("dir", "", "Directory to watch, defaults to current")
	url           = flag.String("url", "", "URL to open")
	watchRegex    = flag.String("watch", `/\w[\w\.]*": (CREATE|MODIFY)`, "Regex to match watch event, use -v to see all events")
	webServer     = flag.String("web", "", "Start a web server at this address, e.g. :8420")
	shell         = flag.String("shell", "", "Shell used to run commands, defaults to $SHELL, fallback to /bin/sh")
)

type Watcher struct {
	Event chan fmt.Stringer
	Error chan error
}

func main() {
	// the following function calls merely serve to logically organize what
	// is otherwise a VERY lengthy setup

	// TODO: have configSetup return a config object so that the reliance on
	// config globals is removed
	configSetup()

	startWebServer()

	runChain(newWatcher(), make(chan struct{}))
}

func runChain(watcher *Watcher, quit chan struct{}) {
	chain := make([]Runnable, 0, 5)

	// build chain of runnables
	if len(*buildCmd) > 0 {
		chain = append(chain, NewRunWait(*buildCmd))
	}
	if len(*daemonCmd) > 0 {
		if len(*daemonTrigger) > 0 {
			chain = append(chain, NewDaemonTrigger(*daemonCmd, *daemonTrigger))
		} else {
			chain = append(chain, NewDaemonTimer(*daemonCmd, *daemonTimer))
		}
	}
	if len(*postCmd) > 0 {
		chain = append(chain, NewRunWait(*postCmd))
	}
	if *url != "" {
		chain = append(chain, NewBrowser(*url))
	}

	eventRegex, err := regexp.Compile(*watchRegex)
	if err != nil {
		log.Fatal("Watch regex compile error:", err)(1)
	}

	var wg sync.WaitGroup

	// main loop
	for {
		// all channels of struct{} are disposable, single use
		// kill is passed to all Runnable so they know when they should exit
		kill := make(chan struct{})
		// abort is to single the event loop that a Runnable did not succeed so cancel everything
		abort := make(chan struct{})

		var drain func()
		drain = func() {
			select {
			case ev := <-watcher.Event:
				log.Debug("Extra event ignored:", ev.String())
				drain()
			default:
			}
		}
		drain()

		// event loop
		go func() {
			for {
				select {
				case ev := <-watcher.Event:
					if eventRegex.MatchString(ev.String()) {
						log.Info("Matched event:", ev.String())
						close(kill)
						return
					} else {
						log.Debug("Ignored event:", ev.String())
					}
				case err = <-watcher.Error:
					log.Fatal("Watcher error:", err)(5)
				case <-abort:
					close(kill)
					return
				case <-quit:
					// currently only used by test suite
					close(kill)
					return
				}
			}
		}()

	RunLoop:
		for _, runnable := range chain {
			done, dead := runnable(kill)
			wg.Add(1)

			go func() {
				wg.Done()
				<-dead
			}()

			select {
			case d := <-done:
				if !d {
					// Runnable failed it's success metric
					close(abort)
					break RunLoop
				}
			case <-kill:
				break RunLoop
			}
		}

		// ensure an event has occured, we may be here because all runnables completed
		<-kill

		// ensure all runnables (procs) are dead before restarting the chain
		wg.Wait()

		// check if we should quit, currently only used by test suites for teardown
		select {
		case <-quit:
			log.Warn("Quitting run chain")
			return
		default:
		}
	}
}

func newWatcher() *Watcher {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}

	watchDir := func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			return nil
		}

		log.Debug("Watching dir:", path)

		if err != nil {
			log.Err("Skipping dir:", path, err)
			return filepath.SkipDir
		}

		err = watcher.Watch(path)
		if err != nil {
			panic(err)
		}

		return nil
	}

	if *recursive == true {
		err = filepath.Walk(*targetDir, watchDir)
		if err != nil {
			panic(err)
		}
	} else {
		err = watcher.Watch(*targetDir)
		if err != nil {
			panic(err)
		}
	}

	// To facilitate testing (which sends artifical events from a timer),
	// we have an abstracted struct Watcher that holds the applicable channels.
	// fsnotify.FileEvent is a fmt.Stringer, but channels cannot be converted.
	// Unfortunately, an extra channel is necessary to perform the conversion.
	event := make(chan fmt.Stringer)
	go func() {
		for {
			event <- <-watcher.Event
		}
	}()

	return &Watcher{event, watcher.Error}
}

func startWebServer() {
	if *webServer != "" {
		go func() {
			log.Info("Starting web server on port", *webServer)
			err := http.ListenAndServe(*webServer, http.FileServer(http.Dir(*targetDir)))
			if err != nil {
				log.Fatal("Error starting web server:", err)(2)
			}
		}()
	}
}

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

	flag.Usage = func() {
		fmt.Println("WaGo (Watch, Go) build tool. Usage:")
		flag.PrintDefaults()
	}

	// TODO: this should check for actions
	if len(os.Args) < 2 {
		flag.Usage()
		log.Fatal("You must specify an action")(1)
	}

	flag.Parse()

	if *verbose {
		log = dog.NewDog(dog.DEBUG)
	} else if *quiet {
		log = dog.NewDog(dog.WARN)
	} else {
		log = dog.NewDog(dog.INFO)
	}

	if len(*shell) == 0 {
		*shell = os.Getenv("SHELL")
		if len(*shell) == 0 {
			*shell = "/bin/sh"
		}
	}
	log.Debug("Using shell", *shell)

	if (len(*daemonTrigger) > 0) && (*daemonTimer > 0) {
		log.Fatal("Both daemon trigger and timer specified, use only one")(1)
	}

	if (len(*daemonTrigger) > 0 || *daemonTimer > 0) && len(*daemonCmd) == 0 {
		log.Fatal("Specify a daemon command to use the trigger or timer")(1)
	}

	if len(*buildCmd) == 0 && len(*daemonCmd) == 0 && !*fiddle && len(*postCmd) == 0 && len(*url) == 0 && len(*webServer) == 0 {
		flag.Usage()
		log.Fatal("You must specify an action")(1)
	}

	if *fiddle {
		if *webServer == "" {
			*webServer = ":9933"
		}
		if *url == "" {
			*url = "http://localhost" + *webServer + "/"
		}
	}

	if *targetDir == "" {
		cwd, err := os.Getwd()
		if err != nil {
			panic(err)
		}
		targetDir = &cwd
	}
	log.Debug("Target dir:", *targetDir)
}
<|endoftext|>


<commit_before>package gorequest

import (
	"bytes"
	"encoding/json"
	"fmt"
	_ "io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type Response *http.Response

type SuperAgent struct {
	Url       string
	Method    string
	Header    map[string]string
	Type      string
	ForceType string
	Data      map[string]interface{}
	FormData  url.Values
	QueryData url.Values
}

func New() *SuperAgent {
	s := SuperAgent{
		Type:   "json",
		Data:   make(map[string]interface{}),
		Header: make(map[string]string)}
	return &s
}

func Get(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "GET",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		FormData:  url.Values{},
		QueryData: url.Values{}}
	return newReq
}

func Post(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "POST",
		Type:      "json",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		FormData:  url.Values{},
		QueryData: url.Values{}}
	return newReq
}

func (s *SuperAgent) Set(param string, value string) *SuperAgent {
	s.Header[param] = value
	return s
}

var Types = map[string]string{
	"html":       "text/html",
	"json":       "application/json",
	"xml":        "application/xml",
	"urlencoded": "application/x-www-form-urlencoded",
	"form":       "application/x-www-form-urlencoded",
	"form-data":  "application/x-www-form-urlencoded",
}

func (s *SuperAgent) SetType(typeStr string) *SuperAgent {
	if _, ok := Types[typeStr]; ok {
		s.ForceType = typeStr
	}
	return s
}

// TODO: check error
func (s *SuperAgent) Query(content string) *SuperAgent {
	var val map[string]string
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.QueryData.Add(k, v)
		}
	} else {
		queryVal, _ := url.ParseQuery(content)
		for k, _ := range queryVal {
			s.QueryData.Add(k, queryVal.Get(k))
		}
		// TODO: need to check correct format of 'field=val&field=val&...'
	}
	return s
}

func (s *SuperAgent) Send(content string) *SuperAgent {
	var val map[string]interface{}
	// check if it is json format
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.Data[k] = v
		}
	} else {
		formVal, _ := url.ParseQuery(content)
		for k, _ := range formVal {
			// make it array if already have key
			if val, ok := s.Data[k]; ok {
				var strArray []string
				strArray = append(strArray, formVal.Get(k))
				// check if previous data is one string or array
				switch oldValue := val.(type) {
				case []string:
					strArray = append(strArray, oldValue...)
				case string:
					strArray = append(strArray, oldValue)
				}
				s.Data[k] = strArray
			} else {
				// make it just string if does not already have same key
				s.Data[k] = formVal.Get(k)
			}
		}
		s.Type = "form"
	}
	return s
}

func changeMapToURLValues(data map[string]interface{}) url.Values {
	var newUrlValues = url.Values{}
	for k, v := range data {
		switch val := v.(type) {
		case string:
			newUrlValues.Add(k, string(val))
		case []string:
			for _, element := range val {
				newUrlValues.Add(k, element)
			}
		}
	}
	return newUrlValues
}

func (s *SuperAgent) End(callback ...func(response Response)) (Response, error) {
	var (
		req  *http.Request
		err  error
		resp Response
	)
	client := &http.Client{}
	// check if there is forced type
	if s.ForceType == "json" {
		s.Type = "json"
	} else if s.ForceType == "form" {
		s.Type = "form"
	}
	if s.Method == "POST" {
		if s.Type == "json" {
			contentJson, _ := json.Marshal(s.Data)
			contentReader := bytes.NewReader(contentJson)
			req, err = http.NewRequest(s.Method, s.Url, contentReader)
			req.Header.Set("Content-Type", "application/json")
		} else if s.Type == "form" {
			formData := changeMapToURLValues(s.Data)
			req, err = http.NewRequest(s.Method, s.Url, strings.NewReader(formData.Encode()))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
	} else if s.Method == "GET" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	}
	for k, v := range s.Header {
		req.Header.Set(k, v)
	}
	// Add all querystring from Query func
	q := req.URL.Query()
	for k, v := range s.QueryData {
		for _, vv := range v {
			q.Add(k, vv)
		}
	}
	req.URL.RawQuery = q.Encode()
	// Send request
	resp, err = client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// deep copy response to give it to both return and callback func
	respCallback := *resp
	if len(callback) != 0 {
		callback[0](&respCallback)
	}
	return resp, nil
}

func main() {
	/*err, response, body:= Get("http://localhost:1337")
	  if err==nil && response.StatusCode == 200 {
	    fmt.Println(body)
	  }
	  fmt.Println(err, response, body)*/

	//s.post("/api/pet").send(`{"name":"tg"}`).end(
	Post("http://requestb.in/1f7ur5s1").
		Send(`nickname=a`).
		Set("Accept", "application/json").
		End(func(response Response) {
		fmt.Println(response)
	})
	/*client:= &http.Client{}
	  req,_ := http.NewRequest("GET", "http://localhost:1337", nil)
	  req.Header.Add("Content-Type","application/json")
	  fmt.Println("main",req)
	  res, _ :=  client.Do(req)
	  fmt.Println(res.Body)
	  /*const jsonStream =`{"sn":"sn1"}`
	  reader:=strings.NewReader(jsonStream)
	  resp,_ := http.Post("http://localhost:1337", "application/json", reader)
	  defer resp.Body.Close()
	  body,_ :=ioutil.ReadAll(resp.Body)
	  fmt.Println(resp)
	  fmt.Println(string(body))*/
}
<commit_msg>Deleted unmeaningful comment<commit_after>package gorequest

import (
	"bytes"
	"encoding/json"
	"fmt"
	_ "io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type Response *http.Response

type SuperAgent struct {
	Url       string
	Method    string
	Header    map[string]string
	Type      string
	ForceType string
	Data      map[string]interface{}
	FormData  url.Values
	QueryData url.Values
}

func New() *SuperAgent {
	s := SuperAgent{
		Type:   "json",
		Data:   make(map[string]interface{}),
		Header: make(map[string]string)}
	return &s
}

func Get(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "GET",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		FormData:  url.Values{},
		QueryData: url.Values{}}
	return newReq
}

func Post(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "POST",
		Type:      "json",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		FormData:  url.Values{},
		QueryData: url.Values{}}
	return newReq
}

func (s *SuperAgent) Set(param string, value string) *SuperAgent {
	s.Header[param] = value
	return s
}

var Types = map[string]string{
	"html":       "text/html",
	"json":       "application/json",
	"xml":        "application/xml",
	"urlencoded": "application/x-www-form-urlencoded",
	"form":       "application/x-www-form-urlencoded",
	"form-data":  "application/x-www-form-urlencoded",
}

func (s *SuperAgent) SetType(typeStr string) *SuperAgent {
	if _, ok := Types[typeStr]; ok {
		s.ForceType = typeStr
	}
	return s
}

// TODO: check error
func (s *SuperAgent) Query(content string) *SuperAgent {
	var val map[string]string
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.QueryData.Add(k, v)
		}
	} else {
		queryVal, _ := url.ParseQuery(content)
		for k, _ := range queryVal {
			s.QueryData.Add(k, queryVal.Get(k))
		}
		// TODO: need to check correct format of 'field=val&field=val&...'
	}
	return s
}

func (s *SuperAgent) Send(content string) *SuperAgent {
	var val map[string]interface{}
	// check if it is json format
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		for k, v := range val {
			s.Data[k] = v
		}
	} else {
		formVal, _ := url.ParseQuery(content)
		for k, _ := range formVal {
			// make it array if already have key
			if val, ok := s.Data[k]; ok {
				var strArray []string
				strArray = append(strArray, formVal.Get(k))
				// check if previous data is one string or array
				switch oldValue := val.(type) {
				case []string:
					strArray = append(strArray, oldValue...)
				case string:
					strArray = append(strArray, oldValue)
				}
				s.Data[k] = strArray
			} else {
				// make it just string if does not already have same key
				s.Data[k] = formVal.Get(k)
			}
		}
		s.Type = "form"
	}
	return s
}

func changeMapToURLValues(data map[string]interface{}) url.Values {
	var newUrlValues = url.Values{}
	for k, v := range data {
		switch val := v.(type) {
		case string:
			newUrlValues.Add(k, string(val))
		case []string:
			for _, element := range val {
				newUrlValues.Add(k, element)
			}
		}
	}
	return newUrlValues
}

func (s *SuperAgent) End(callback ...func(response Response)) (Response, error) {
	var (
		req  *http.Request
		err  error
		resp Response
	)
	client := &http.Client{}
	// check if there is forced type
	if s.ForceType == "json" {
		s.Type = "json"
	} else if s.ForceType == "form" {
		s.Type = "form"
	}
	if s.Method == "POST" {
		if s.Type == "json" {
			contentJson, _ := json.Marshal(s.Data)
			contentReader := bytes.NewReader(contentJson)
			req, err = http.NewRequest(s.Method, s.Url, contentReader)
			req.Header.Set("Content-Type", "application/json")
		} else if s.Type == "form" {
			formData := changeMapToURLValues(s.Data)
			req, err = http.NewRequest(s.Method, s.Url, strings.NewReader(formData.Encode()))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
	} else if s.Method == "GET" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	}
	for k, v := range s.Header {
		req.Header.Set(k, v)
	}
	// Add all querystring from Query func
	q := req.URL.Query()
	for k, v := range s.QueryData {
		for _, vv := range v {
			q.Add(k, vv)
		}
	}
	req.URL.RawQuery = q.Encode()
	// Send request
	resp, err = client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// deep copy response to give it to both return and callback func
	respCallback := *resp
	if len(callback) != 0 {
		callback[0](&respCallback)
	}
	return resp, nil
}

func main() {
	Post("http://requestb.in/1f7ur5s1").
		Send(`nickname=a`).
		Set("Accept", "application/json").
		End(func(response Response) {
		fmt.Println(response)
	})
}
<|endoftext|>


<commit_before>package main

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

	"github.com/d2r2/go-dht"
	// For controlling Orvibo stuff
	"github.com/pusnik/go-orvibo"
)

func monitorHumidity(hum *PiHumidifier) {
	sensorType := dht.DHT22
	temperature, humidity, retried, err :=
		dht.ReadDHTxxWithRetry(sensorType, 3, false, 10)
	if err != nil {
		log.Fatal(err)
	}
	// print temperature and humidity
	fmt.Printf("Temperature = %v*C, Humidity = %v%% (retried %d times)\n",
		temperature, humidity, retried)
}

//  States we can be in at any point in time
type stateT int

const (
	stateSTART                = stateT(iota)
	stateCONFIG               // start configuration
	stateCONFIGSCAN           // scan for smart sockets
	stateCONFIGSETSOCKET      // set which socket do we want to control
	stateCONFIGSETTHRESHOLD   // set thresholds for humidifier
	stateMONITOR              // in monitor mode
	stateMONITORREADTEMPHUMID // read values from socket
	stateMONITORSAVETODB      // save data to db to analize
	stateMONITORCONTROLSOCKET // turn on/off the socket
)

type piHumidifierInterface interface {
	config()
	discoverSockets() map[string]*orvibo.Device
	monitorHumidity()
}

//PiHumidifier is our main class
type PiHumidifier struct {
	state        stateT //  Current state
	socket       *orvibo.Device
	devices      map[string]*orvibo.Device
	humidityLow  int
	humidityHigh int
}

func (hum *PiHumidifier) config() {
	reader := bufio.NewReader(os.Stdin)
	fmt.Print("What can I do for you?\n")

	for { // Loop forever
		command, err := reader.ReadString('\n')
		if err != nil {
			log.Fatal(err)
		}
		command = strings.ToLower(strings.TrimSpace(command))

		if command == "discover" {
			fmt.Println("Discover")
			hum.state = stateCONFIGSCAN
			return
		} else if command == "exit" || command == "no" {
			os.Exit(3)
		} else if command == "monitor" {
			hum.state = stateMONITORREADTEMPHUMID
		} else {
			fmt.Println("Unknown command!")
		}
		fmt.Println("Done! Do you want anything else?")
	}
}

func (hum *PiHumidifier) discoverSockets() map[string]*orvibo.Device {
	timeoutChan := time.NewTimer(time.Second * 5).C

	ready, err := orvibo.Prepare() // You ready?
	if ready == true {
		fmt.Println("Searching...")

		orvibo.Discover() // Discover all sockets

		//start go routine to check for messages
		go func() {
			for {
				orvibo.CheckForMessages()
			}
		}()

		for { // Loop forever
			select { // This lets us do non-blocking channel reads. If we have a message, process it. If not, check for UDP data and loop
			case <-timeoutChan:
				orvibo.Close()
				return orvibo.Devices
			case msg := <-orvibo.Events:
				switch msg.Name {
				case "socketfound":
					fmt.Println("---------------")
					fmt.Println("Found:", msg.DeviceInfo.IP)
					fmt.Println("MAC:", msg.DeviceInfo.MACAddress)
				}
			}
		}
	} else {
		fmt.Println("Error:", err)
	}
	return orvibo.Devices
}

func (hum *PiHumidifier) executeFsm() {
	fmt.Println("In FSM")
	for {
		switch hum.state {
		case stateSTART:
			fmt.Println("State Start")
			hum.config()
		case stateCONFIGSCAN:
			hum.devices = hum.discoverSockets()
			hum.state = stateSTART
		case stateMONITOR:
			fmt.Println("State monitor")
			hum.monitorHumidity()
		}
	}
}

func main() {
	fmt.Println("Starting")
	hum := &PiHumidifier{humidityHigh: 60, humidityLow: 35, state: stateSTART, socket: nil}
	hum.executeFsm()
}
<commit_msg>added monitoring<commit_after>package main

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

	"github.com/d2r2/go-dht"
	// For controlling Orvibo stuff
	"github.com/pusnik/go-orvibo"
)

//  States we can be in at any point in time
type stateT int

const (
	stateSTART                = stateT(iota)
	stateCONFIG               // start configuration
	stateCONFIGSCAN           // scan for smart sockets
	stateCONFIGSETSOCKET      // set which socket do we want to control
	stateCONFIGSETTHRESHOLD   // set thresholds for humidifier
	stateMONITOR              // in monitor mode
	stateMONITORREADTEMPHUMID // read values from socket
	stateMONITORSAVETODB      // save data to db to analize
	stateMONITORCONTROLSOCKET // turn on/off the socket
)

type piHumidifierInterface interface {
	config()
	discoverSockets() map[string]*orvibo.Device
	monitorHumidity()
}

//PiHumidifier is our main class
type PiHumidifier struct {
	state        stateT //  Current state
	socket       *orvibo.Device
	devices      map[string]*orvibo.Device
	humidityLow  int
	humidityHigh int
}

func (hum *PiHumidifier)  monitorHumidity() {
        sensorType := dht.DHT22
        temperature, humidity, retried, err :=
                dht.ReadDHTxxWithRetry(sensorType, 3, false, 10)
        if err != nil {
                log.Fatal(err)
        }
        // print temperature and humidity
        fmt.Printf("Temperature = %v*C, Humidity = %v%% (retried %d times)\n",
                temperature, humidity, retried)
}

func (hum *PiHumidifier) config() {
	reader := bufio.NewReader(os.Stdin)
	fmt.Print("What can I do for you?\n")

	for { // Loop forever
		command, err := reader.ReadString('\n')
		if err != nil {
			log.Fatal(err)
		}
		command = strings.ToLower(strings.TrimSpace(command))

		if command == "discover" {
			fmt.Println("Discover")
			hum.state = stateCONFIGSCAN
			return
		} else if command == "exit" || command == "no" {
			os.Exit(3)
		} else if command == "monitor" {
			hum.state = stateMONITORREADTEMPHUMID
			return
		} else {
			fmt.Println("Unknown command!")
		}
		fmt.Println("Done! Do you want anything else?")
	}
}

func (hum *PiHumidifier) discoverSockets() map[string]*orvibo.Device {
	timeoutChan := time.NewTimer(time.Second * 5).C

	ready, err := orvibo.Prepare() // You ready?
	if ready == true {
		fmt.Println("Searching...")

		orvibo.Discover() // Discover all sockets

		//start go routine to check for messages
		go func() {
			for {
				orvibo.CheckForMessages()
			}
		}()

		for { // Loop forever
			select { // This lets us do non-blocking channel reads. If we have a message, process it. If not, check for UDP data and loop
			case <-timeoutChan:
				orvibo.Close()
				return orvibo.Devices
			case msg := <-orvibo.Events:
				switch msg.Name {
				case "socketfound":
					fmt.Println("---------------")
					fmt.Println("Found:", msg.DeviceInfo.IP)
					fmt.Println("MAC:", msg.DeviceInfo.MACAddress)
				}
			}
		}
	} else {
		fmt.Println("Error:", err)
	}
	return orvibo.Devices
}

func (hum *PiHumidifier) executeFsm() {
	fmt.Println("In FSM")
	for {
		switch hum.state {
		case stateSTART:
			fmt.Println("State Start")
			hum.config()
		case stateCONFIGSCAN:
			hum.devices = hum.discoverSockets()
			hum.state = stateSTART
		case stateMONITORREADTEMPHUMID:
			fmt.Println("State monitor")
			hum.monitorHumidity()
		}
	}
}

func main() {
	fmt.Println("Starting")
	hum := &PiHumidifier{humidityHigh: 60, humidityLow: 35, state: stateSTART, socket: nil}
	hum.executeFsm()
}
<|endoftext|>


<commit_before>package main

import (
	"fileutils"
	"flag"
	"fmt"
	"image/png"
	"io/ioutil"
	"os"
	"scummatlas"
	l "scummatlas/condlog"
	"scummatlas/templates"
	"strings"
)

const REQUIRED string = "REQUIRED"

var gamedir string
var outputdir string
var singleRoom int
var noimages bool

func main() {
	loadOptions()

	_, err := ioutil.ReadDir(outputdir)
	if err != nil {
		err := os.Mkdir(outputdir, 0755)
		if err != nil {
			fmt.Println("Output directory doesn't exist and I can't create it.")
			os.Exit(1)
		}
	}
	os.MkdirAll(outputdir+"/img_obj", 0755)
	os.MkdirAll(outputdir+"/img_bg", 0755)

	game := scummatlas.NewGame(gamedir)
	if singleRoom < 1 {
		game.ProcessAllRooms(outputdir)
	} else {
		game.ProcessSingleRoom(singleRoom, outputdir)
	}

	templates.WriteIndex(game, outputdir)
	templates.WriteTable(game, outputdir)

	copyStaticFiles()

	if singleRoom > 0 {
		processRoom(game.Rooms[singleRoom])
	} else {
		for _, room := range game.Rooms {
			processRoom(room)
		}
	}

}

func loadOptions() {
	var logflags string

	logkeys := make([]string, 0, len(l.Flags))
	for k := range l.Flags {
		logkeys = append(logkeys, k)
	}
	flag.StringVar(&gamedir, "gamedir", REQUIRED, "Directory with the game files")
	flag.StringVar(&outputdir, "outputdir", REQUIRED, "Directory to put the generated files in")
	flag.IntVar(&singleRoom, "room", 0, "Only parse one room")
	flag.BoolVar(&noimages, "noimages", false, "Don't create images")
	flag.StringVar(&logflags, "logflags", "", "Comma separated list of log flags. Available flags: "+strings.Join(logkeys, ", "))
	flag.Parse()

	if outputdir == REQUIRED || gamedir == REQUIRED {
		fmt.Println("outputdir and gamedir are not optional\n")
		flag.PrintDefaults()
		os.Exit(1)
	}

	flags := strings.Split(logflags, ",")
	for _, flag := range flags {
		l.Flags[flag] = true
	}
}

func processRoom(room scummatlas.Room) {
	fmt.Println("Generate files for room ", room.Id)
	templates.WriteRoom(room, outputdir)
	if noimages {
		return
	}
	writeRoomBackground(room, outputdir)
	createRoomObjectImages(room)
	for _, obj := range room.Objects {
		obj.PrintVerbs()
	}
}

func copyStaticFiles() {
	fileutils.CopyDir("./static", outputdir+"/static")
}

func writeRoomBackground(room scummatlas.Room, outputdir string) {
	backgroundFile := fmt.Sprintf("%v/img_bg/room%02d_bg", outputdir, room.Id)
	l.Log("template", "\nWriting room %v background in %v\n", room.Id, backgroundFile+".png")
	pngFile, err := os.Create(backgroundFile + ".png")
	if err != nil {
		panic("Error creating " + backgroundFile + ".png")
	}
	png.Encode(pngFile, room.Image)

	for i, zplane := range room.Zplanes {
		filename := fmt.Sprintf("%v-zplane%d.png", backgroundFile, i+1)
		pngFile, err := os.Create(filename)
		if err != nil {
			panic("Error creating " + filename)
		}
		png.Encode(pngFile, zplane)
	}
}

func createRoomObjectImages(r scummatlas.Room) {
	for _, object := range r.Objects {
		if len(object.Image.Frames) == 0 {
			//fmt.Printf("Obj %v does not have an image\n", object.Id)
			continue
		}

		for frameIndex, frame := range object.Image.Frames {
			if frame == nil {
				continue
			}
			imagePath := fmt.Sprintf(
				"%v/img_obj/room%02d_obj_%02x_%02d.png",
				outputdir,
				r.Id,
				object.Id,
				frameIndex)

			pngFile, err := os.Create(imagePath)
			if err != nil {
				panic("Error creating " + imagePath)
			}
			png.Encode(pngFile, frame)
			l.Log("template", "Obj image %v created\n", imagePath)
		}
	}
}
<commit_msg>async<commit_after>package main

import (
	"fileutils"
	"flag"
	"sync"
	"fmt"
	"image/png"
	"io/ioutil"
	"os"
	"scummatlas"
	l "scummatlas/condlog"
	"scummatlas/templates"
	"strings"
)

const REQUIRED string = "REQUIRED"

var gamedir string
var outputdir string
var singleRoom int
var noimages bool

func main() {
	loadOptions()

	_, err := ioutil.ReadDir(outputdir)
	if err != nil {
		err := os.Mkdir(outputdir, 0755)
		if err != nil {
			fmt.Println("Output directory doesn't exist and I can't create it.")
			os.Exit(1)
		}
	}
	os.MkdirAll(outputdir+"/img_obj", 0755)
	os.MkdirAll(outputdir+"/img_bg", 0755)

	game := scummatlas.NewGame(gamedir)
	if singleRoom < 1 {
		game.ProcessAllRooms(outputdir)
	} else {
		game.ProcessSingleRoom(singleRoom, outputdir)
	}

	templates.WriteIndex(game, outputdir)
	templates.WriteTable(game, outputdir)

	copyStaticFiles()

	if singleRoom > 0 {
		processRoom(game.Rooms[singleRoom])
	} else {
		var wg sync.WaitGroup
		messages := make(chan int)
		wg.Add(len(game.Rooms))
		for _, room := range game.Rooms {
			go func(room scummatlas.Room) {
				defer wg.Done()
				processRoom(room)
				messages <- room.Id
			}(room)
		}
		go func() {
			for i := range messages {
				fmt.Printf("Room %v processed\n",i)
			}
		}()
		wg.Wait()
	}

}

func loadOptions() {
	var logflags string

	logkeys := make([]string, 0, len(l.Flags))
	for k := range l.Flags {
		logkeys = append(logkeys, k)
	}
	flag.StringVar(&gamedir, "gamedir", REQUIRED, "Directory with the game files")
	flag.StringVar(&outputdir, "outputdir", REQUIRED, "Directory to put the generated files in")
	flag.IntVar(&singleRoom, "room", 0, "Only parse one room")
	flag.BoolVar(&noimages, "noimages", false, "Don't create images")
	flag.StringVar(&logflags, "logflags", "", "Comma separated list of log flags. Available flags: "+strings.Join(logkeys, ", "))
	flag.Parse()

	if outputdir == REQUIRED || gamedir == REQUIRED {
		fmt.Println("outputdir and gamedir are not optional\n")
		flag.PrintDefaults()
		os.Exit(1)
	}

	flags := strings.Split(logflags, ",")
	for _, flag := range flags {
		l.Flags[flag] = true
	}
}

func processRoom(room scummatlas.Room) {
	templates.WriteRoom(room, outputdir)
	if noimages {
		return
	}
	writeRoomBackground(room, outputdir)
	createRoomObjectImages(room)
	for _, obj := range room.Objects {
		obj.PrintVerbs()
	}
}

func copyStaticFiles() {
	fileutils.CopyDir("./static", outputdir+"/static")
}

func writeRoomBackground(room scummatlas.Room, outputdir string) {
	backgroundFile := fmt.Sprintf("%v/img_bg/room%02d_bg", outputdir, room.Id)
	l.Log("template", "\nWriting room %v background in %v\n", room.Id, backgroundFile+".png")
	pngFile, err := os.Create(backgroundFile + ".png")
	if err != nil {
		panic("Error creating " + backgroundFile + ".png")
	}
	png.Encode(pngFile, room.Image)

	for i, zplane := range room.Zplanes {
		filename := fmt.Sprintf("%v-zplane%d.png", backgroundFile, i+1)
		pngFile, err := os.Create(filename)
		if err != nil {
			panic("Error creating " + filename)
		}
		png.Encode(pngFile, zplane)
	}
}

func createRoomObjectImages(r scummatlas.Room) {
	for _, object := range r.Objects {
		if len(object.Image.Frames) == 0 {
			//fmt.Printf("Obj %v does not have an image\n", object.Id)
			continue
		}

		for frameIndex, frame := range object.Image.Frames {
			if frame == nil {
				continue
			}
			imagePath := fmt.Sprintf(
				"%v/img_obj/room%02d_obj_%02x_%02d.png",
				outputdir,
				r.Id,
				object.Id,
				frameIndex)

			pngFile, err := os.Create(imagePath)
			if err != nil {
				panic("Error creating " + imagePath)
			}
			png.Encode(pngFile, frame)
			l.Log("template", "Obj image %v created\n", imagePath)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/hashicorp/hcl"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type graphQLHTTPRequestBody struct {
	Query         string `json:"query"`
	Variables     string `json:"variables"`
	OperationName string `json:"operationName"`
}

type graphQLHTTPResponseBody struct {
	Data map[string][]graphQLResponseData `json:"data"`
}

type graphQLResponseData struct {
	PricePerUnit string `json:"PricePerUnit"`
	Unit         string `json:"Unit"`
	Currency     string `json:"Currency"`
}

var knownResourceTypes = map[string]string{
	// Using query aliases to get the pricing data for different types of instances at the same time
	"aws_instance": "%s: AmazonEC2(Location:\"%s\", TermType:\"OnDemand\", InstanceType:\"%s\", OS:\"Linux\", Tenancy:\"Shared\") {PricePerUnit Unit Currency}",
}

var resourceTypesToFriendlyNames = map[string]string{
	"aws_instance": "EC2",
}

var regionMap = map[string]string{
	"us-gov-west-1":  "AWS GovCloud (US)",
	"us-east-1":      "US East (N. Virginia)",
	"us-east-2":      "US East (Ohio)",
	"us-west-1":      "US West (N. California)",
	"us-west-2":      "US West (Oregon)",
	"ca-central-1":   "Canada (Central)",
	"eu-west-1":      "EU (Ireland)",
	"eu-central-1":   "EU (Frankfurt)",
	"eu-west-2":      "EU (London)",
	"ap-northeast-1": "Asia Pacific (Tokyo)",
	"ap-northeast-2": "Asia Pacific (Seoul)",
	"ap-southeast-1": "Asia Pacific (Singapore)",
	"ap-southeast-2": "Asia Pacific (Sydney)",
	"ap-south-1":     "Asia Pacific (Mumbai)",
	"sa-east-1":      "South America (Sao Paulo)",
}

// See https://github.com/Bjorn248/graphql_aws_pricing_api for the code of this API
const apiUrl = "https://fvaexi95f8.execute-api.us-east-1.amazonaws.com/Dev/graphql"

func main() {
	// notest

	if os.Getenv("AWS_REGION") == "" {
		log.Fatal("AWS_REGION not set")
	}

	pattern := ""
	if os.Getenv("GLOB_PATTERN") == "" {
		pattern = "*.tf"
	} else {
		pattern = os.Getenv("GLOB_PATTERN")
	}

	matches, err := filepath.Glob(pattern)
	if err != nil {
		fmt.Printf("Error reading files: '%s'", err)
	}

	masterResourceMap := map[string]map[string]int{
		"aws_instance": {"r4.xlarge": 0},
	}

	for _, filePath := range matches {
		processTerraformFile(masterResourceMap, filePath)
	}

	graphQLQueryString, err := generateGraphQLQuery(masterResourceMap)
	if err != nil {
		fmt.Printf("Error generating GraphQL Query: '%s'", err)
	}

	// We want a high timeout because the lambda function
	// needs at least 1 request to warm up. The first request
	// always takes a long time.
	timeout := time.Duration(40 * time.Second)

	client := http.Client{
		Timeout: timeout,
	}

	fmt.Println("Calling GraphQL Pricing API...")

	resp, err := client.Post(apiUrl, "application/json", bytes.NewBuffer([]byte(graphQLQueryString)))
	if err != nil {
		fmt.Printf("Error making request to Pricing API: '%s'", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("Error reading response body: '%s'", err)
	}
	var response graphQLHTTPResponseBody

	unmarshalErr := json.Unmarshal(body, &response)
	if unmarshalErr != nil {
		log.Fatal("Error Unmarshalling Response Body", unmarshalErr)
	}

	resourceCostMap, err := calculateInfraCost(response, masterResourceMap)
	if err != nil {
		log.Fatal("Error generating resourceCostMap", err)
	}

	for resource, hourlyCost := range resourceCostMap {
		fmt.Println("")
		fmt.Println("Cost of", resourceTypesToFriendlyNames[resource])
		fmt.Printf("Hourly: $%v\nMonthly: $%v\nNote: Monthly Cost based on 730 hours on average being in a month\n", hourlyCost, hourlyCost*float32(730))
	}
}

// This function takes the pricing data and uses it to calculate the infrastructure cost by looking at the
// map of terraform resources. Basically, we're just iterating over some maps here...
func calculateInfraCost(pricingData graphQLHTTPResponseBody, terraformResources map[string]map[string]int) (map[string]float32, error) {
	resourceCostMap := map[string]float32{
		"aws_instance": 0.00,
	}

	for resourceName, resourceTypes := range terraformResources {
		resourceCostMap[resourceName] = 0.00
		for resourceType, count := range resourceTypes {
			alias := strings.Replace(resourceType, ".", "_", -1)
			oldValue := resourceCostMap[resourceName]
			var price float64
			var err error
			for _, element := range pricingData.Data[alias] {
				price, err = strconv.ParseFloat(element.PricePerUnit, 32)
				if err != nil {
					return resourceCostMap, err
				}
			}
			resourceCostMap[resourceName] = oldValue + (float32(price) * float32(count))
		}
	}
	return resourceCostMap, nil
}

// This takes a terraform file and adds it to the global resource map used to shape the GraphQL query
func processTerraformFile(masterResourceMap map[string]map[string]int, filePath string) {
	file, err := ioutil.ReadFile(filePath)
	if err != nil {
		fmt.Printf("Error reading file: '%s'", err)
	}

	var decodedOutput map[string]interface{}

	unmarshalErr := hcl.Unmarshal(file, &decodedOutput)
	if unmarshalErr != nil {
		fmt.Printf("Error decoding HCL: '%s'", unmarshalErr)
	}

	arrayOfResources, success := decodedOutput["resource"].([]map[string]interface{})

	if success == true {
		for _, resource := range arrayOfResources {
			for key := range resource {
				switch key {
				case "aws_instance":
					resourceKeys, resourceKeysSuccess := resource[key].([]map[string]interface{})
					if resourceKeysSuccess == true {
						for resourceKey := range resourceKeys[0] {
							instanceType, instanceTypeSuccess := resourceKeys[0][resourceKey].([]map[string]interface{})[0]["instance_type"].(string)
							if instanceTypeSuccess == true {
								countResource(masterResourceMap, key, instanceType)
							}
						}
					}
				default:
					fmt.Println("resource type not recognized: ", key)
				}
			}
		}
	}
}

// This doesn't need a pointer, or to return anything, because maps in go are always passed by reference
func countResource(resourceMap map[string]map[string]int, resourceName string, resourceType string) {
	if count := resourceMap[resourceName][resourceType]; count == 0 {
		resourceMap[resourceName][resourceType] = 1
	} else {
		resourceMap[resourceName][resourceType] = count + 1
	}
}

func generateGraphQLQuery(masterResourceMap map[string]map[string]int) (string, error) {
	graphQLQueryString := ""
	requestBody := graphQLHTTPRequestBody{
		Query:         "",
		Variables:     "",
		OperationName: "",
	}
	for resource := range masterResourceMap {
		if queryStringTemplate, ok := knownResourceTypes[resource]; ok {
			for resourceType, count := range masterResourceMap[resource] {
				if count > 0 {
					region := regionMap[os.Getenv("AWS_REGION")]
					alias := strings.Replace(resourceType, ".", "_", -1)
					graphQLQueryString = graphQLQueryString + " " + fmt.Sprintf(queryStringTemplate, alias, region, resourceType)
				}
			}
		}
	}
	graphQLQueryString = "{" + graphQLQueryString + "}"
	requestBody.Query = graphQLQueryString
	requestBodyJSON, err := json.Marshal(requestBody)
	if err != nil {
		return "", err
	}
	return string(requestBodyJSON), nil
}
<commit_msg>getting code coverage to 100%<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/hashicorp/hcl"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type graphQLHTTPRequestBody struct {
	Query         string `json:"query"`
	Variables     string `json:"variables"`
	OperationName string `json:"operationName"`
}

type graphQLHTTPResponseBody struct {
	Data map[string][]graphQLResponseData `json:"data"`
}

type graphQLResponseData struct {
	PricePerUnit string `json:"PricePerUnit"`
	Unit         string `json:"Unit"`
	Currency     string `json:"Currency"`
}

var knownResourceTypes = map[string]string{
	// Using query aliases to get the pricing data for different types of instances at the same time
	"aws_instance": "%s: AmazonEC2(Location:\"%s\", TermType:\"OnDemand\", InstanceType:\"%s\", OS:\"Linux\", Tenancy:\"Shared\") {PricePerUnit Unit Currency}",
}

var resourceTypesToFriendlyNames = map[string]string{
	"aws_instance": "EC2",
}

var regionMap = map[string]string{
	"us-gov-west-1":  "AWS GovCloud (US)",
	"us-east-1":      "US East (N. Virginia)",
	"us-east-2":      "US East (Ohio)",
	"us-west-1":      "US West (N. California)",
	"us-west-2":      "US West (Oregon)",
	"ca-central-1":   "Canada (Central)",
	"eu-west-1":      "EU (Ireland)",
	"eu-central-1":   "EU (Frankfurt)",
	"eu-west-2":      "EU (London)",
	"ap-northeast-1": "Asia Pacific (Tokyo)",
	"ap-northeast-2": "Asia Pacific (Seoul)",
	"ap-southeast-1": "Asia Pacific (Singapore)",
	"ap-southeast-2": "Asia Pacific (Sydney)",
	"ap-south-1":     "Asia Pacific (Mumbai)",
	"sa-east-1":      "South America (Sao Paulo)",
}

// See https://github.com/Bjorn248/graphql_aws_pricing_api for the code of this API
const apiUrl = "https://fvaexi95f8.execute-api.us-east-1.amazonaws.com/Dev/graphql"

func main() {
	// notest

	if os.Getenv("AWS_REGION") == "" {
		log.Fatal("AWS_REGION not set")
	}

	pattern := ""
	if os.Getenv("GLOB_PATTERN") == "" {
		pattern = "*.tf"
	} else {
		pattern = os.Getenv("GLOB_PATTERN")
	}

	matches, err := filepath.Glob(pattern)
	if err != nil {
		fmt.Printf("Error reading files: '%s'", err)
	}

	masterResourceMap := map[string]map[string]int{
		"aws_instance": {"r4.xlarge": 0},
	}

	for _, filePath := range matches {
		processTerraformFile(masterResourceMap, filePath)
	}

	graphQLQueryString, err := generateGraphQLQuery(masterResourceMap)
	if err != nil {
		fmt.Printf("Error generating GraphQL Query: '%s'", err)
	}

	// We want a high timeout because the lambda function
	// needs at least 1 request to warm up. The first request
	// always takes a long time.
	timeout := time.Duration(40 * time.Second)

	client := http.Client{
		Timeout: timeout,
	}

	fmt.Println("Calling GraphQL Pricing API...")

	resp, err := client.Post(apiUrl, "application/json", bytes.NewBuffer([]byte(graphQLQueryString)))
	if err != nil {
		fmt.Printf("Error making request to Pricing API: '%s'", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("Error reading response body: '%s'", err)
	}
	var response graphQLHTTPResponseBody

	unmarshalErr := json.Unmarshal(body, &response)
	if unmarshalErr != nil {
		log.Fatal("Error Unmarshalling Response Body", unmarshalErr)
	}

	resourceCostMap, err := calculateInfraCost(response, masterResourceMap)
	if err != nil {
		log.Fatal("Error generating resourceCostMap", err)
	}

	for resource, hourlyCost := range resourceCostMap {
		fmt.Println("")
		fmt.Println("Cost of", resourceTypesToFriendlyNames[resource])
		fmt.Printf("Hourly: $%v\nMonthly: $%v\nNote: Monthly Cost based on 730 hours on average being in a month\n", hourlyCost, hourlyCost*float32(730))
	}
}

// This function takes the pricing data and uses it to calculate the infrastructure cost by looking at the
// map of terraform resources. Basically, we're just iterating over some maps here...
func calculateInfraCost(pricingData graphQLHTTPResponseBody, terraformResources map[string]map[string]int) (map[string]float32, error) {
	resourceCostMap := map[string]float32{
		"aws_instance": 0.00,
	}

	for resourceName, resourceTypes := range terraformResources {
		resourceCostMap[resourceName] = 0.00
		for resourceType, count := range resourceTypes {
			alias := strings.Replace(resourceType, ".", "_", -1)
			oldValue := resourceCostMap[resourceName]
			var price float64
			var err error
			for _, element := range pricingData.Data[alias] {
				price, err = strconv.ParseFloat(element.PricePerUnit, 32)
				if err != nil {
					return resourceCostMap, err
				}
			}
			resourceCostMap[resourceName] = oldValue + (float32(price) * float32(count))
		}
	}
	return resourceCostMap, nil
}

// This takes a terraform file and adds it to the global resource map used to shape the GraphQL query
func processTerraformFile(masterResourceMap map[string]map[string]int, filePath string) {
	file, err := ioutil.ReadFile(filePath)
	if err != nil {
		fmt.Printf("Error reading file: '%s'", err) // notest
	}

	var decodedOutput map[string]interface{}

	unmarshalErr := hcl.Unmarshal(file, &decodedOutput)
	if unmarshalErr != nil {
		fmt.Printf("Error decoding HCL: '%s'", unmarshalErr) // notest
	}

	arrayOfResources, success := decodedOutput["resource"].([]map[string]interface{})

	if success == true {
		for _, resource := range arrayOfResources {
			for key := range resource {
				switch key {
				case "aws_instance":
					resourceKeys, resourceKeysSuccess := resource[key].([]map[string]interface{})
					if resourceKeysSuccess == true {
						for resourceKey := range resourceKeys[0] {
							instanceType, instanceTypeSuccess := resourceKeys[0][resourceKey].([]map[string]interface{})[0]["instance_type"].(string)
							if instanceTypeSuccess == true {
								countResource(masterResourceMap, key, instanceType)
							}
						}
					}
				default:
					fmt.Println("resource type not recognized: ", key)
				}
			}
		}
	}
}

// This doesn't need a pointer, or to return anything, because maps in go are always passed by reference
func countResource(resourceMap map[string]map[string]int, resourceName string, resourceType string) {
	if count := resourceMap[resourceName][resourceType]; count == 0 {
		resourceMap[resourceName][resourceType] = 1
	} else {
		resourceMap[resourceName][resourceType] = count + 1
	}
}

func generateGraphQLQuery(masterResourceMap map[string]map[string]int) (string, error) {
	graphQLQueryString := ""
	requestBody := graphQLHTTPRequestBody{
		Query:         "",
		Variables:     "",
		OperationName: "",
	}
	for resource := range masterResourceMap {
		if queryStringTemplate, ok := knownResourceTypes[resource]; ok {
			for resourceType, count := range masterResourceMap[resource] {
				if count > 0 {
					region := regionMap[os.Getenv("AWS_REGION")]
					alias := strings.Replace(resourceType, ".", "_", -1)
					graphQLQueryString = graphQLQueryString + " " + fmt.Sprintf(queryStringTemplate, alias, region, resourceType)
				}
			}
		}
	}
	graphQLQueryString = "{" + graphQLQueryString + "}"
	requestBody.Query = graphQLQueryString
	requestBodyJSON, err := json.Marshal(requestBody)
	if err != nil {
		return "", err
	}
	return string(requestBodyJSON), nil
}
<|endoftext|>


<commit_before>package main

import "flag"
import "fmt"
import "math/rand"
import "os"
import "time"

func main() {

	// Figure out the width and height of the world.
		stdinFd := int(os.Stdout.Fd())

		terminalWidth, terminalHeight, err := TerminalSize(stdinFd)
		if nil != err {
			// Could not get the terminal width and height. So just default to 80x24.
			terminalWidth, terminalHeight = 80, 24
		}

		var worldWidth  int
		var worldHeight int

		flag.IntVar(&worldWidth  , "width"  , terminalWidth  , "the width of the world")
		flag.IntVar(&worldHeight , "height" , terminalHeight , "the height of the world")

	// Initialize the world.
		world := NewWorld(worldWidth, worldHeight)

	// Set up world.
		var unrandom int

		flag.IntVar(&unrandom, "unrandom", 3, "each cell is initially alive, with a probability of 1/unrandom")

		randomness := rand.New(rand.NewSource( time.Now().UnixNano() ))

		for y:=0; y<terminalHeight; y++ {
			for x:=0; x<terminalWidth; x++ {

				if 0 == randomness.Intn(unrandom) {
					world.Set(x,y, '\u2588')
				}

			}
		}

	// Make time progress.
		fmt.Print("\x0c") // form feed character -- ^L
		fmt.Print(world)
		for {
			time.Sleep(350 * time.Millisecond)

			world.Step()
			fmt.Print("\x0c") // form feed character -- ^L
			fmt.Print(world)
		}
}

<commit_msg>added longer comments to main.go to explain what is happening, in a more detailed manner.<commit_after>package main

import "flag"
import "fmt"
import "math/rand"
import "os"
import "time"

func main() {

	// Figure out the width and height of the world.
	//
	// We do this by first trying to get the width and height of the terminal.
	//
	// (Note that, if for whatever reason, we cannot get the width and height
	// of the terminal, then we just default the terminal width and height to
	// the well known (metaphorical) "least common denominator" of 80x24.
	//
	// Then, after that we check to see if the user has specified a width and
	// height.
	//
	// So, we use the terminal width and height as default values (for the
	// width and height) and let the user override those values, if they
	// wish.
	//
	// Although, it is expected that most of the time, the user will probably
	// just want the terminal width and height, and won't override them.
		stdinFd := int(os.Stdout.Fd())

		terminalWidth, terminalHeight, err := TerminalSize(stdinFd)
		if nil != err {
			// Could not get the terminal width and height. So just default to 80x24.
			terminalWidth, terminalHeight = 80, 24
		}

		var worldWidth  int
		var worldHeight int

		flag.IntVar(&worldWidth  , "width"  , terminalWidth  , "the width of the world")
		flag.IntVar(&worldHeight , "height" , terminalHeight , "the height of the world")

	// Initialize the world.
		world := NewWorld(worldWidth, worldHeight)

	// Set up world.
	//
	// When we start of, all the cells are dead. To see anything interestng happen,
	// we need to make some of these cells alive.
	//
	// So, we do through each cell, and then use a pseudo-random number generator to
	// help us decide whether to make the cell alive or not.
	//
	// Essentially, each cell has a 1/unrandom chance of being made alive. Note that
	// "unrandom" is a variable. So, for example, if unrandin is 3, then each cell
	// has a probability of 1/3 of being made alive -- has a 1 in 3 chance of being
	// made alive.
		var unrandom int

		flag.IntVar(&unrandom, "unrandom", 3, "each cell is initially alive, with a probability of 1/unrandom")

		randomness := rand.New(rand.NewSource( time.Now().UnixNano() ))

		for y:=0; y<terminalHeight; y++ {
			for x:=0; x<terminalWidth; x++ {

				if 0 == randomness.Intn(unrandom) {
					world.Set(x,y, '\u2588')
				}

			}
		}

	// Make time progress.
		fmt.Print("\x0c") // form feed character -- ^L
		fmt.Print(world)
		for {
			time.Sleep(350 * time.Millisecond)

			world.Step()
			fmt.Print("\x0c") // form feed character -- ^L
			fmt.Print(world)
		}
}

<|endoftext|>


<commit_before>package store

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/prometheus/alertmanager/types"
	"github.com/prometheus/common/model"
)

var (
	// ErrNotFound is returned if a Store cannot find the Alert.
	ErrNotFound = errors.New("alert not found")
)

// Alerts provides lock-coordinated to an in-memory map of alerts, keyed by
// their fingerprint. Resolved alerts are removed from the map based on
// gcInterval. An optional callback can be set which receives a slice of all
// resolved alerts that have been removed.
type Alerts struct {
	gcInterval time.Duration

	sync.Mutex
	c  map[model.Fingerprint]*types.Alert
	cb func([]*types.Alert)
}

// NewAlerts returns a new Alerts struct.
func NewAlerts(gcInterval time.Duration) *Alerts {
	a := &Alerts{
		c:          make(map[model.Fingerprint]*types.Alert),
		cb:         func(_ []*types.Alert) {},
		gcInterval: gcInterval,
	}

	if gcInterval == 0 {
		gcInterval = time.Minute
	}

	return a
}

// SetGCCallback sets a GC callback to be executed after each GC.
func (a *Alerts) SetGCCallback(cb func([]*types.Alert)) {
	a.Lock()
	defer a.Unlock()

	a.cb = cb
}

// Run starts the GC loop.
func (a *Alerts) Run(ctx context.Context) {
	go func(t *time.Ticker) {
		for {
			select {
			case <-ctx.Done():
				return
			case <-t.C:
				a.gc()
			}
		}
	}(time.NewTicker(a.gcInterval))
}

func (a *Alerts) gc() {
	a.Lock()
	defer a.Unlock()

	resolved := []*types.Alert{}
	for fp, alert := range a.c {
		if alert.Resolved() {
			delete(a.c, fp)
			resolved = append(resolved, alert)
		}
	}
	a.cb(resolved)
}

// Get returns the Alert with the matching fingerprint, or an error if it is
// not found.
func (a *Alerts) Get(fp model.Fingerprint) (*types.Alert, error) {
	a.Lock()
	defer a.Unlock()

	alert, prs := a.c[fp]
	if !prs {
		return nil, ErrNotFound
	}
	return alert, nil
}

// Set unconditionally sets the alert in memory.
func (a *Alerts) Set(alert *types.Alert) error {
	a.Lock()
	defer a.Unlock()

	a.c[alert.Fingerprint()] = alert
	return nil
}

// Delete removes the Alert with the matching fingerprint from the store.
func (a *Alerts) Delete(fp model.Fingerprint) error {
	a.Lock()
	defer a.Unlock()

	delete(a.c, fp)
	return nil
}

// List returns a buffered channel of Alerts currently held in memory.
func (a *Alerts) List() <-chan *types.Alert {
	a.Lock()
	defer a.Unlock()

	c := make(chan *types.Alert, len(a.c))
	for _, alert := range a.c {
		c <- alert
	}
	close(c)

	return c
}

// Count returns the number of items within the store.
func (a *Alerts) Count() int {
	a.Lock()
	defer a.Unlock()

	return len(a.c)
}
<commit_msg>store: Fix ineffectual assignment for default GC interval (#1568)<commit_after>package store

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/prometheus/alertmanager/types"
	"github.com/prometheus/common/model"
)

var (
	// ErrNotFound is returned if a Store cannot find the Alert.
	ErrNotFound = errors.New("alert not found")
)

// Alerts provides lock-coordinated to an in-memory map of alerts, keyed by
// their fingerprint. Resolved alerts are removed from the map based on
// gcInterval. An optional callback can be set which receives a slice of all
// resolved alerts that have been removed.
type Alerts struct {
	gcInterval time.Duration

	sync.Mutex
	c  map[model.Fingerprint]*types.Alert
	cb func([]*types.Alert)
}

// NewAlerts returns a new Alerts struct.
func NewAlerts(gcInterval time.Duration) *Alerts {
	if gcInterval == 0 {
		gcInterval = time.Minute
	}

	a := &Alerts{
		c:          make(map[model.Fingerprint]*types.Alert),
		cb:         func(_ []*types.Alert) {},
		gcInterval: gcInterval,
	}

	return a
}

// SetGCCallback sets a GC callback to be executed after each GC.
func (a *Alerts) SetGCCallback(cb func([]*types.Alert)) {
	a.Lock()
	defer a.Unlock()

	a.cb = cb
}

// Run starts the GC loop.
func (a *Alerts) Run(ctx context.Context) {
	go func(t *time.Ticker) {
		for {
			select {
			case <-ctx.Done():
				return
			case <-t.C:
				a.gc()
			}
		}
	}(time.NewTicker(a.gcInterval))
}

func (a *Alerts) gc() {
	a.Lock()
	defer a.Unlock()

	resolved := []*types.Alert{}
	for fp, alert := range a.c {
		if alert.Resolved() {
			delete(a.c, fp)
			resolved = append(resolved, alert)
		}
	}
	a.cb(resolved)
}

// Get returns the Alert with the matching fingerprint, or an error if it is
// not found.
func (a *Alerts) Get(fp model.Fingerprint) (*types.Alert, error) {
	a.Lock()
	defer a.Unlock()

	alert, prs := a.c[fp]
	if !prs {
		return nil, ErrNotFound
	}
	return alert, nil
}

// Set unconditionally sets the alert in memory.
func (a *Alerts) Set(alert *types.Alert) error {
	a.Lock()
	defer a.Unlock()

	a.c[alert.Fingerprint()] = alert
	return nil
}

// Delete removes the Alert with the matching fingerprint from the store.
func (a *Alerts) Delete(fp model.Fingerprint) error {
	a.Lock()
	defer a.Unlock()

	delete(a.c, fp)
	return nil
}

// List returns a buffered channel of Alerts currently held in memory.
func (a *Alerts) List() <-chan *types.Alert {
	a.Lock()
	defer a.Unlock()

	c := make(chan *types.Alert, len(a.c))
	for _, alert := range a.c {
		c <- alert
	}
	close(c)

	return c
}

// Count returns the number of items within the store.
func (a *Alerts) Count() int {
	a.Lock()
	defer a.Unlock()

	return len(a.c)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"time"

	"net/http"

	"github.com/mitchellh/go-ps"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var procTotalMem = prometheus.NewGaugeVec(
	prometheus.GaugeOpts{
		Namespace: "node",
		Subsystem: "os",
		Name:      "process_memory",
		Help:      "Number of bytes allocated by process name (sum of all matching)",
	},
	[]string{"procname"})

func calculateMemory(pid int) (uint64, error) {
	f, err := os.Open(fmt.Sprintf("/proc/%d/smaps", pid))
	if err != nil {
		return 0, err
	}
	defer f.Close()

	res := uint64(0)
	pfx := []byte("Pss:")
	r := bufio.NewScanner(f)
	for r.Scan() {
		line := r.Bytes()
		if bytes.HasPrefix(line, pfx) {
			var size uint64
			_, err := fmt.Sscanf(string(line[4:]), "%d", &size)
			if err != nil {
				return 0, err
			}
			res += size
		}
	}
	if err := r.Err(); err != nil {
		return 0, err
	}

	return res, nil
}

func updateProcessCounters() {
	var procSizes = make(map[string]uint64)

	procs, err := ps.Processes()

	if err != nil {
		log.Print("Error fetching process list: ", err)
		return
	}

	for _, proc := range procs {
		size, err := calculateMemory(proc.Pid())
		if err == nil {
			procSizes[proc.Executable()] += size
		} else {
			log.Print("Error calculating memory for ", proc.Executable(), ": ",
				err)
		}
	}

	for name, size := range procSizes {
		procTotalMem.WithLabelValues(name).Set(float64(size))
	}
}

func processTicker(t <-chan time.Time) {
	for range t {
		updateProcessCounters()
	}
}

func main() {
	var listen string
	var port int

	flag.StringVar(&listen, "listen", "",
		"Host name to bind to")
	flag.IntVar(&port, "port", 0,
		"Assign specific port to the listener")
	flag.Parse()

	prometheus.MustRegister(procTotalMem)

	go processTicker(time.Tick(1 * time.Minute))
	http.Handle("/metrics", promhttp.Handler())
	log.Fatal(http.ListenAndServe(
		net.JoinHostPort(listen, strconv.Itoa(port)), nil))
}
<commit_msg>Update process counters before the first minute is over.<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"time"

	"net/http"

	"github.com/mitchellh/go-ps"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var procTotalMem = prometheus.NewGaugeVec(
	prometheus.GaugeOpts{
		Namespace: "node",
		Subsystem: "os",
		Name:      "process_memory",
		Help:      "Number of bytes allocated by process name (sum of all matching)",
	},
	[]string{"procname"})

func calculateMemory(pid int) (uint64, error) {
	f, err := os.Open(fmt.Sprintf("/proc/%d/smaps", pid))
	if err != nil {
		return 0, err
	}
	defer f.Close()

	res := uint64(0)
	pfx := []byte("Pss:")
	r := bufio.NewScanner(f)
	for r.Scan() {
		line := r.Bytes()
		if bytes.HasPrefix(line, pfx) {
			var size uint64
			_, err := fmt.Sscanf(string(line[4:]), "%d", &size)
			if err != nil {
				return 0, err
			}
			res += size
		}
	}
	if err := r.Err(); err != nil {
		return 0, err
	}

	return res, nil
}

func updateProcessCounters() {
	var procSizes = make(map[string]uint64)

	procs, err := ps.Processes()

	if err != nil {
		log.Print("Error fetching process list: ", err)
		return
	}

	for _, proc := range procs {
		size, err := calculateMemory(proc.Pid())
		if err == nil {
			procSizes[proc.Executable()] += size
		} else {
			log.Print("Error calculating memory for ", proc.Executable(), ": ",
				err)
		}
	}

	for name, size := range procSizes {
		procTotalMem.WithLabelValues(name).Set(float64(size))
	}
}

func processTicker(t <-chan time.Time) {
	updateProcessCounters()
	for range t {
		updateProcessCounters()
	}
}

func main() {
	var listen string
	var port int

	flag.StringVar(&listen, "listen", "",
		"Host name to bind to")
	flag.IntVar(&port, "port", 0,
		"Assign specific port to the listener")
	flag.Parse()

	prometheus.MustRegister(procTotalMem)

	go processTicker(time.Tick(1 * time.Minute))
	http.Handle("/metrics", promhttp.Handler())
	log.Fatal(http.ListenAndServe(
		net.JoinHostPort(listen, strconv.Itoa(port)), nil))
}
<|endoftext|>


<commit_before><commit_msg>make call.done channel buffered so it can discard its value if the call got cancelled<commit_after><|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"mellium.im/sasl"
	"mellium.im/xmlstream"
	"mellium.im/xmpp"
	"mellium.im/xmpp/dial"
	"mellium.im/xmpp/jid"
	"mellium.im/xmpp/stanza"
	"os"
)

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

type MessageBody struct {
	stanza.Message
	Body string `xml:"body"`
}

func initXMPP(address jid.JID, pass string, skipTLSVerify bool, legacyTLS bool, forceStartTLS bool) (*xmpp.Session, error) {
	tlsConfig := tls.Config{InsecureSkipVerify: skipTLSVerify}
	var dialer dial.Dialer
	if skipTLSVerify {
		dialer = dial.Dialer{NoTLS: !legacyTLS, TLSConfig: &tlsConfig}
	} else {
		dialer = dial.Dialer{NoTLS: !legacyTLS}
	}
	conn, err := dialer.Dial(context.TODO(), "tcp", address)
	if err != nil {
		return nil, err
	}
	if !skipTLSVerify {
		tlsConfig.ServerName = address.Domainpart()
	}
	return xmpp.NegotiateSession(
		context.TODO(),
		address.Domain(),
		address,
		conn,
		false,
		xmpp.NewNegotiator(xmpp.StreamConfig{Features: []xmpp.StreamFeature{
			xmpp.BindResource(),
			xmpp.StartTLS(forceStartTLS, &tlsConfig),
			xmpp.SASL("", pass, sasl.ScramSha1Plus, sasl.ScramSha1, sasl.Plain),
		}}),
	)
}

func closeXMPP(session *xmpp.Session) {
	session.Close()
	session.Conn().Close()
}

func main() {
	// get xmpp credentials, message receivers
	xi := os.Getenv("XMPP_ID")
	xp := os.Getenv("XMPP_PASS")
	xr := os.Getenv("XMPP_RECEIVERS")

	// get tls settings from env
	_, skipTLSVerify := os.LookupEnv("XMPP_SKIP_VERIFY")
	_, legacyTLS := os.LookupEnv("XMPP_OVER_TLS")
	_, forceStartTLS := os.LookupEnv("XMPP_FORCE_STARTTLS")

	// check if xmpp credentials and receiver list are supplied
	if xi == "" || xp == "1" || xr == "" {
		log.Fatal("XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set")
	}

	address, err := jid.Parse(xi)
	panicOnErr(err)

	// connect to xmpp server
	session, err := initXMPP(address, xp, skipTLSVerify, legacyTLS, forceStartTLS)
	panicOnErr(err)
	defer closeXMPP(session)

	// send initial presence
	panicOnErr(session.Send(context.TODO(), stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil)))

	err = session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
		d := xml.NewTokenDecoder(t)
		if start.Name.Local != "message" {
			return nil
		}

		msg := MessageBody{}
		err = d.DecodeElement(&msg, start)
		if err != nil && err != io.EOF {
			return nil
		}

		if msg.Body == "" || msg.Type != stanza.ChatMessage {
			return nil
		}

		reply := MessageBody{
			Message: stanza.Message{
				To: msg.From.Bare(),
			},
			Body: msg.Body,
		}

		err = t.Encode(reply)
		if err != nil {
			fmt.Printf("Error responding to message %q: %q", msg.ID, err)
		}
		return nil
	}))

	panicOnErr(err)

	/*// create chan for messages (webhooks -> xmpp)
	messages := make(chan string)

	// wait for messages from the webhooks and send them to all receivers
	go func() {
		for m := range messages {
			for _, r := range strings.Split(xr, ",") {
				xc.Out <- xmpp.Message{
					To: r,
					Body: []xmpp.MessageBody{
						{
							Value: m,
						},
					},
				}
			}
		}
	}()

	// initialize handler for grafana alerts
	http.Handle("/grafana", newMessageHandler(messages, grafanaParserFunc))

	// listen for requests
	http.ListenAndServe(":4321", nil)*/
}
<commit_msg>Handle alerts with mellium-xmpp (WiP)<commit_after>package main

import (
	"context"
	"crypto/tls"
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"mellium.im/sasl"
	"mellium.im/xmlstream"
	"mellium.im/xmpp"
	"mellium.im/xmpp/dial"
	"mellium.im/xmpp/jid"
	"mellium.im/xmpp/stanza"
	"net/http"
	"os"
	"strings"
)

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

type MessageBody struct {
	stanza.Message
	Body string `xml:"body"`
}

func initXMPP(address jid.JID, pass string, skipTLSVerify bool, legacyTLS bool, forceStartTLS bool) (*xmpp.Session, error) {
	tlsConfig := tls.Config{InsecureSkipVerify: skipTLSVerify}
	var dialer dial.Dialer
	// only use the tls config for the dialer if necessary
	if skipTLSVerify {
		dialer = dial.Dialer{NoTLS: !legacyTLS, TLSConfig: &tlsConfig}
	} else {
		dialer = dial.Dialer{NoTLS: !legacyTLS}
	}
	conn, err := dialer.Dial(context.TODO(), "tcp", address)
	if err != nil {
		return nil, err
	}
	// we need the domain in the tls config if we want to verify the cert
	if !skipTLSVerify {
		tlsConfig.ServerName = address.Domainpart()
	}
	return xmpp.NegotiateSession(
		context.TODO(),
		address.Domain(),
		address,
		conn,
		false,
		xmpp.NewNegotiator(xmpp.StreamConfig{Features: []xmpp.StreamFeature{
			xmpp.BindResource(),
			xmpp.StartTLS(forceStartTLS, &tlsConfig),
			xmpp.SASL("", pass, sasl.ScramSha1Plus, sasl.ScramSha1, sasl.Plain),
		}}),
	)
}

func closeXMPP(session *xmpp.Session) {
	session.Close()
	session.Conn().Close()
}

func main() {
	// get xmpp credentials, message receivers
	xi := os.Getenv("XMPP_ID")
	xp := os.Getenv("XMPP_PASS")
	xr := os.Getenv("XMPP_RECEIVERS")

	// get tls settings from env
	_, skipTLSVerify := os.LookupEnv("XMPP_SKIP_VERIFY")
	_, legacyTLS := os.LookupEnv("XMPP_OVER_TLS")
	_, forceStartTLS := os.LookupEnv("XMPP_FORCE_STARTTLS")

	// check if xmpp credentials and receiver list are supplied
	if xi == "" || xp == "1" || xr == "" {
		log.Fatal("XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set")
	}

	address, err := jid.Parse(xi)
	panicOnErr(err)

	// connect to xmpp server
	xmppSession, err := initXMPP(address, xp, skipTLSVerify, legacyTLS, forceStartTLS)
	panicOnErr(err)
	defer closeXMPP(xmppSession)

	// send initial presence
	panicOnErr(xmppSession.Send(context.TODO(), stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil)))

	// listen for messages and echo them
	go func() {
		err = xmppSession.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
			d := xml.NewTokenDecoder(t)
			// ignore elements that aren't messages
			if start.Name.Local != "message" {
				return nil
			}

			// parse message into struct
			msg := MessageBody{}
			err = d.DecodeElement(&msg, start)
			if err != nil && err != io.EOF {
				return nil
			}

			// ignore empty messages and stanzas that aren't messages
			if msg.Body == "" || msg.Type != stanza.ChatMessage {
				return nil
			}

			// create reply with identical contents
			reply := MessageBody{
				Message: stanza.Message{
					To: msg.From.Bare(),
				},
				Body: msg.Body,
			}

			// try to send reply, ignore errors
			_ = t.Encode(reply)
			return nil
		}))
		panicOnErr(err)
	}()

	// create chan for messages (webhooks -> xmpp)
	messages := make(chan string)

	// wait for messages from the webhooks and send them to all receivers
	go func() {
		for m := range messages {
			for _, r := range strings.Split(xr, ",") {
				recipient, err := jid.Parse(r)
				panicOnErr(err)
				fmt.Println(m)
				fmt.Println(recipient)
				// try to send message, ignore errors
				//_ = xmppSession.Send(context.TODO(), stanza.WrapMessage(recipient, stanza.NormalMessage))
			}
		}
	}()

	// initialize handler for grafana alerts
	http.Handle("/grafana", newMessageHandler(messages, grafanaParserFunc))

	// listen for requests
	http.ListenAndServe(":4321", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var queue chan string

func animeList(w http.ResponseWriter, r *http.Request) {
	log.Print("anime list requested")
	seriesPage, err := FetchPageContents(UrlPseudoJoin("/"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	series, err := ExtractSeriesList(seriesPage)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	j, _ := json.Marshal(series)
	w.Write(j)
	log.Print("anime list request fulfilled")
}

func animeDetail(w http.ResponseWriter, r *http.Request) {
	pathSplit := strings.Split(r.URL.Path, "/")
	if len(pathSplit[2]) < 1 {
		http.Error(w, "you must specify animeme", http.StatusBadRequest)
		log.Print("episodes list requested for unspecified anime")
		return
	}
	log.Printf("episodes list requested for %v", pathSplit[2])
	episodesPage, err := FetchPageContents(UrlPseudoJoin("/a/" + pathSplit[2]))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	episodes, err := ExtractEpisodesList(episodesPage)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	j, _ := json.Marshal(episodes)
	w.Write(j)
	log.Printf("episodes list for %v fulfilled", pathSplit[2])
}

func enqueue(url string) bool {
	select {
	case queue <- url:
		log.Print("enqueued:", url)
		return true
	default:
		return false
	}
}

func externPlay(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")
	if len(url) <3 {
		http.Error(w, "you must specify url parameter", http.StatusBadRequest)
		log.Print("extern request without url")
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	if !enqueue(url) {
		http.Error(w, "queue full", http.StatusTooManyRequests)
		log.Print("queue full for", url)
		return
	}
	w.Write([]byte("{}"))
}

func animePlay(w http.ResponseWriter, r *http.Request) {
	pathSplit := strings.Split(r.URL.Path, "/")
	if len(pathSplit) < 4 {
		http.Error(w, "you must specify animeme and episode number", http.StatusBadRequest)
		return
	}
	if len(pathSplit[2]) < 1 {
		http.Error(w, "you must specify animeme", http.StatusBadRequest)
		return
	}
	if len(pathSplit[3]) < 1 {
		http.Error(w, "you must specify episode", http.StatusBadRequest)
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	url := UrlPseudoJoin("/a/" + r.URL.Path[len("/play/"):])
	if !enqueue(url) {
		http.Error(w, "queue full", http.StatusTooManyRequests)
		log.Print("queue full for", url)
		return
	}
	w.Write([]byte("{}"))
}

var VideoSrcRegexp = regexp.MustCompile(`<video src="([^"]+?)"`)

func ExtractTwistVideo(body string) string {
	match := VideoSrcRegexp.FindStringSubmatch(body)
	if match != nil {
		return UrlPseudoJoin(match[1])
	}
	return ""
}

func handleQueue() {
	for {
		url := <-queue
		log.Print("extracting url from ", url, " for omx")
		var vurl string = ""
		if strings.HasPrefix(url, TwistRoot) {
			page, err := FetchPageContents(url)
			if err == nil {
				vurl = ExtractTwistVideo(page)
			} else {
				log.Print("extrwction via twist methodics failed,", err)
			}
		}
		if vurl == "" {
			vvurl, err := exec.Command("youtube-dl", "-g", url).Output()
			if err != nil {
				vurl = url
				log.Print("extraction unsuccessful, trying default")
			} else {
				vurl = strings.TrimSpace(string(vvurl))
				log.Print("extraction result: ", vurl)
			}
		}
		log.Print("starting to play")
		omx := exec.Command("omxplayer", vurl)
		omx.Stdout = os.Stderr
		omx.Stdin = os.Stdin
		omx.Stderr = os.Stderr
		if omx.Run() != nil {
			exec.Command("mpv", "-v", "--fs", url).Run()
		}
		log.Print("playback finished")
	}
}

func main() {
	queue = make(chan string, 100)
	go handleQueue()
	http.HandleFunc("/list", animeList)
	http.HandleFunc("/detail/", animeDetail)
	http.HandleFunc("/play/", animePlay)
	http.HandleFunc("/extern/", externPlay)
	http.Handle("/", http.FileServer(http.Dir("static")))
	log.Print("launching")
	http.ListenAndServe(":3000", nil)
}
<commit_msg>heuristic for local files to skip yt-dl resolution<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var queue chan string

func animeList(w http.ResponseWriter, r *http.Request) {
	log.Print("anime list requested")
	seriesPage, err := FetchPageContents(UrlPseudoJoin("/"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	series, err := ExtractSeriesList(seriesPage)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	j, _ := json.Marshal(series)
	w.Write(j)
	log.Print("anime list request fulfilled")
}

func animeDetail(w http.ResponseWriter, r *http.Request) {
	pathSplit := strings.Split(r.URL.Path, "/")
	if len(pathSplit[2]) < 1 {
		http.Error(w, "you must specify animeme", http.StatusBadRequest)
		log.Print("episodes list requested for unspecified anime")
		return
	}
	log.Printf("episodes list requested for %v", pathSplit[2])
	episodesPage, err := FetchPageContents(UrlPseudoJoin("/a/" + pathSplit[2]))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	episodes, err := ExtractEpisodesList(episodesPage)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	j, _ := json.Marshal(episodes)
	w.Write(j)
	log.Printf("episodes list for %v fulfilled", pathSplit[2])
}

func enqueue(url string) bool {
	select {
	case queue <- url:
		log.Print("enqueued:", url)
		return true
	default:
		return false
	}
}

func externPlay(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")
	if len(url) <3 {
		http.Error(w, "you must specify url parameter", http.StatusBadRequest)
		log.Print("extern request without url")
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	if !enqueue(url) {
		http.Error(w, "queue full", http.StatusTooManyRequests)
		log.Print("queue full for", url)
		return
	}
	w.Write([]byte("{}"))
}

func animePlay(w http.ResponseWriter, r *http.Request) {
	pathSplit := strings.Split(r.URL.Path, "/")
	if len(pathSplit) < 4 {
		http.Error(w, "you must specify animeme and episode number", http.StatusBadRequest)
		return
	}
	if len(pathSplit[2]) < 1 {
		http.Error(w, "you must specify animeme", http.StatusBadRequest)
		return
	}
	if len(pathSplit[3]) < 1 {
		http.Error(w, "you must specify episode", http.StatusBadRequest)
		return
	}
	h := w.Header()
	h.Add("Content-Type", "application/json")
	url := UrlPseudoJoin("/a/" + r.URL.Path[len("/play/"):])
	if !enqueue(url) {
		http.Error(w, "queue full", http.StatusTooManyRequests)
		log.Print("queue full for", url)
		return
	}
	w.Write([]byte("{}"))
}

var VideoSrcRegexp = regexp.MustCompile(`<video src="([^"]+?)"`)

func ExtractTwistVideo(body string) string {
	match := VideoSrcRegexp.FindStringSubmatch(body)
	if match != nil {
		return UrlPseudoJoin(match[1])
	}
	return ""
}

func handleQueue() {
	for {
		url := <-queue
		log.Print("extracting url from ", url, " for omx")
		var vurl string = ""
		if !strings.Contains(url, ":/") {
			log.Print("heuristically assuming it's a local file")
			vurl = url
		} else if strings.HasPrefix(url, TwistRoot) {
			page, err := FetchPageContents(url)
			if err == nil {
				vurl = ExtractTwistVideo(page)
			} else {
				log.Print("extraction via twist methodics failed,", err)
			}
		}
		if vurl == "" {
			vvurl, err := exec.Command("youtube-dl", "-g", url).Output()
			if err != nil {
				vurl = url
				log.Print("extraction unsuccessful, trying default")
			} else {
				vurl = strings.TrimSpace(string(vvurl))
				log.Print("extraction result: ", vurl)
			}
		}
		log.Print("starting to play")
		omx := exec.Command("omxplayer", vurl)
		omx.Stdout = os.Stderr
		omx.Stdin = os.Stdin
		omx.Stderr = os.Stderr
		if omx.Run() != nil {
			exec.Command("mpv", "-v", "--fs", url).Run()
		}
		log.Print("playback finished")
	}
}

func main() {
	queue = make(chan string, 100)
	go handleQueue()
	http.HandleFunc("/list", animeList)
	http.HandleFunc("/detail/", animeDetail)
	http.HandleFunc("/play/", animePlay)
	http.HandleFunc("/extern/", externPlay)
	http.Handle("/", http.FileServer(http.Dir("static")))
	log.Print("launching")
	http.ListenAndServe(":3000", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"encoding/base64"
	"io/ioutil"
	"encoding/json"
	"path"
	"github.com/bitrise-io/go-utils/cmdex"
)

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

func getOptionalEnv(env string) string {
	return os.Getenv(env)
}

func getRequiredEnv(env string) string {
	if len(env) == 0 {
		panic(env + " is not defined!")
	}

	return os.Getenv(env)
}

func isEmpty(str string) bool {
	return len(str) == 0
}

func checkFileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}

type GcloudKeyFile struct {
	ProjectID   string `json:"project_id"`
	ClientEmail string `json:"client_email"`
}

func quote(str string) string {
	return "\"" + str + "\""
}

func main() {
	gcloud_user := ""    //getOptionalEnv("GCLOUD_USER")
	gcloud_project := "" //getOptionalEnv("GCLOUD_PROJECT")

	app_apk := getRequiredEnv("APP_APK")
	checkFileExists(app_apk)

	test_apk := getOptionalEnv("TEST_APK")
	if !isEmpty(test_apk) {
		checkFileExists(test_apk)
	}

	gcloud_key, err := base64.StdEncoding.DecodeString(getRequiredEnv("GCLOUD_KEY"))
	checkError(err)

	empty_gcloud_user := isEmpty(gcloud_user)
	empty_gcloud_project := isEmpty(gcloud_project)

	if empty_gcloud_user || empty_gcloud_project {
		parsedKeyFile := GcloudKeyFile{}
		json.Unmarshal([]byte(gcloud_key), &parsedKeyFile)

		if empty_gcloud_user {
			gcloud_user = parsedKeyFile.ClientEmail
			if isEmpty(gcloud_user) { panic("Missing gcloud user") }
		}

		if empty_gcloud_project {
			gcloud_project = parsedKeyFile.ProjectID
			if isEmpty(gcloud_project) { panic("Missing gcloud project") }
		}
	}

	fmt.Println("User: ", gcloud_user)
	fmt.Println("Project: ", gcloud_project)
	fmt.Println("App APK: ", app_apk)
	fmt.Println("Test APK: ", app_apk)

	home_dir := getRequiredEnv("HOME")
	checkError(ioutil.WriteFile(path.Join(home_dir, "gcloudkey.json"), gcloud_key, 0644))

	/*
		gcloud config set project "$GCLOUD_PROJECT"
		gcloud auth activate-service-account --key-file "$HOME/gcloudkey.json" "$GCLOUD_USER"
	*/

	cmdSlice := []string{"gcloud", "config", "set", "project", quote(gcloud_project)}
	cmd := cmdex.NewCommand(cmdSlice[0], cmdSlice[1:]...)
	cmd.SetStdout(os.Stdout)
	cmd.SetStderr(os.Stderr)
	cmd.Run()

	// TODO: allow configuration options

	/*
    @device_ids = %w[NexusLowRes]
    @api_level  = 25

    if @robo
      type = '--type robo'
    else
      type         = '--type instrumentation'
      test_apk     = %Q(--test "#{ENV['TEST_APK']}")
      sd_card_path = '--directories-to-pull=/sdcard'
    end

    flags = [
        type,
        %Q(--app "#{ENV['APP_APK']}"),
        test_apk,
        "--results-bucket android-#{@app_name}",
        "--device-ids #{@device_ids.join(',')}",
        "--os-version-ids #{@api_level}",
        '--locales en',
        '--orientations portrait',
        '--timeout 25m',
        sd_card_path
    ].reject &:nil?

    flags << %Q(--test-targets "#{@test_targets}") unless @test_targets.empty?

    # must use custom env separator or gcloud CLI will get confused on comma separated annotations
    if @opts[:coverage] || @annotations
      env_vars      = []
      env_vars      += ['coverage=true', 'coverageFile=/sdcard/coverage.ec'] if @opts[:coverage]
      env_vars      += ["annotation=#{@annotations}"] if @annotations
      env_separator = ':'
      flags << "--environment-variables ^#{env_separator}^#{env_vars.join(env_separator)}"
    end

    gcloud firebase test android run FLAGS

    parse bucket link from gcloud CLI

    // download firebase.ec

    bucket = 'gs:/' + bucket_link.split('storage/browser').last
    bucket = "#{bucket}#{@device_ids.first}-#{@api_level}-en-portrait/artifacts/coverage.ec"
    _run_command "gsutil cp #{bucket} /bitrise/src/#{@app_name}/app/build/firebase.ec"
	*/

	// TODO: use exit code from gcloud CLI

	//
	// --- Exit codes:
	// The exit code of your Step is very important. If you return
	//  with a 0 exit code `bitrise` will register your Step as "successful".
	// Any non zero exit code will be registered as "failed" by `bitrise`.
	os.Exit(0)
}
<commit_msg>more progress on bitrise step<commit_after>package main

import (
	"fmt"
	"os"
	"encoding/base64"
	"io/ioutil"
	"encoding/json"
	"path"
	"github.com/bitrise-io/go-utils/command"
	"strings"
)

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

func getOptionalEnv(env string) string {
	return os.Getenv(env)
}

func getRequiredEnv(env string) string {
	if len(env) == 0 {
		panic(env + " is not defined!")
	}

	return os.Getenv(env)
}

func isEmpty(str string) bool {
	return len(str) == 0
}

func checkFileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}

type GcloudKeyFile struct {
	ProjectID   string `json:"project_id"`
	ClientEmail string `json:"client_email"`
}

func runCommand(cmd string) {
	cmdSlice :=  strings.Fields(cmd)

	cmdObj := command.NewWithStandardOuts(cmdSlice[0], cmdSlice[1:]...)
	checkError(cmdObj.Run())
}

func main() {
	// todo: refactor this into a method which populates a struct
	gcloud_user := ""    //getOptionalEnv("GCLOUD_USER")
	gcloud_project := "" //getOptionalEnv("GCLOUD_PROJECT")

	app_apk := getRequiredEnv("APP_APK")
	checkFileExists(app_apk)

	test_apk := getOptionalEnv("TEST_APK")
	if !isEmpty(test_apk) {
		checkFileExists(test_apk)
	}

	gcloud_key, err := base64.StdEncoding.DecodeString(getRequiredEnv("GCLOUD_KEY"))
	checkError(err)

	empty_gcloud_user := isEmpty(gcloud_user)
	empty_gcloud_project := isEmpty(gcloud_project)

	if empty_gcloud_user || empty_gcloud_project {
		parsedKeyFile := GcloudKeyFile{}
		json.Unmarshal([]byte(gcloud_key), &parsedKeyFile)

		if empty_gcloud_user {
			gcloud_user = parsedKeyFile.ClientEmail
			if isEmpty(gcloud_user) {
				panic("Missing gcloud user")
			}
		}

		if empty_gcloud_project {
			gcloud_project = parsedKeyFile.ProjectID
			if isEmpty(gcloud_project) {
				panic("Missing gcloud project")
			}
		}
	}

	fmt.Println("User: ", gcloud_user)
	fmt.Println("Project: ", gcloud_project)
	fmt.Println("App APK: ", app_apk)
	fmt.Println("Test APK: ", app_apk)

	home_dir := getRequiredEnv("HOME")
	key_file_path := path.Join(home_dir, "gcloudkey.json")
	checkError(ioutil.WriteFile(key_file_path, gcloud_key, 0644))

	runCommand("gcloud config set project " + gcloud_project)
	runCommand("gcloud auth activate-service-account --key-file " + key_file_path + " " + gcloud_user )

	// TODO: allow configuration options
	// TODO: how to grab stdout from executed command

	/*
	device_ids := "NexusLowRes"
	api_level := 25

	test_type := "robo"

	if !isEmpty(test_apk) {
		test_type = "instrumentation"
	}
	*/

	/*
    @device_ids = %w[NexusLowRes]
    @api_level  = 25

    if @robo
      type = '--type robo'
    else
      type         = '--type instrumentation'
      test_apk     = %Q(--test "#{ENV['TEST_APK']}")
      sd_card_path = '--directories-to-pull=/sdcard'
    end

    flags = [
        type,
        %Q(--app "#{ENV['APP_APK']}"),
        test_apk,
        "--results-bucket android-#{@app_name}",
        "--device-ids #{@device_ids.join(',')}",
        "--os-version-ids #{@api_level}",
        '--locales en',
        '--orientations portrait',
        '--timeout 25m',
        sd_card_path
    ].reject &:nil?

    flags << %Q(--test-targets "#{@test_targets}") unless @test_targets.empty?

    # must use custom env separator or gcloud CLI will get confused on comma separated annotations
    if @opts[:coverage] || @annotations
      env_vars      = []
      env_vars      += ['coverage=true', 'coverageFile=/sdcard/coverage.ec'] if @opts[:coverage]
      env_vars      += ["annotation=#{@annotations}"] if @annotations
      env_separator = ':'
      flags << "--environment-variables ^#{env_separator}^#{env_vars.join(env_separator)}"
    end

    gcloud firebase test android run FLAGS

    parse bucket link from gcloud CLI

    // download firebase.ec

    bucket = 'gs:/' + bucket_link.split('storage/browser').last
    bucket = "#{bucket}#{@device_ids.first}-#{@api_level}-en-portrait/artifacts/coverage.ec"
    _run_command "gsutil cp #{bucket} /bitrise/src/#{@app_name}/app/build/firebase.ec"
	*/

	// TODO: use exit code from gcloud CLI

	//
	// --- Exit codes:
	// The exit code of your Step is very important. If you return
	//  with a 0 exit code `bitrise` will register your Step as "successful".
	// Any non zero exit code will be registered as "failed" by `bitrise`.
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

type Builtin struct {
	Run func(args []string)
}

var BUILTINS = map[string]*Builtin{
	"cd": &Builtin{func(args []string) {
		os.Chdir(args[0])
	}},
	"exit": &Builtin{func(args []string) {
		var code int
		if len(args) == 1 {
			code, _ = strconv.Atoi(args[0])
		}
		os.Exit(code)
	}},
	"exec": &Builtin{func(args []string) {
		cmd := exec.Command(args[0], args[1:]...)
		spawnPrograms(cmd)
	}},
	"set": &Builtin{func(args []string) {
		for _, arg := range args {
			keyValuePair := strings.Split(arg, "=")
			if len(keyValuePair) == 2 {
				os.Setenv(keyValuePair[0], keyValuePair[1])
			}
		}
	}},
}

func init() {
	os.Setenv("PROMPT", "->")
}

func main() {
	prompt()

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		commands := splitOnPipes(scanner.Text())

		var cmds []*exec.Cmd
		for _, command := range commands {
			name, args := parseCommand(command)
			if name == "" {
				continue
			}

			if isBuiltin(name) {
				callBuiltin(name, args)
			} else {
				cmd := exec.Command(name, args...)
				cmds = append(cmds, cmd)
			}
		}

		if len(cmds) > 0 {
			spawnPrograms(cmds...)
		}

		prompt()
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "Reading standard input:", err)
	}
}

func prompt() {
	fmt.Fprintf(os.Stdout, "%s ", os.Getenv("PROMPT"))
}

func splitOnPipes(line string) (commands []string) {
	pipeRegexp := regexp.MustCompile("([^\"'|]+)|[\"']([^\"']+)[\"']")
	if pipeRegexp.MatchString(line) {
		commands = pipeRegexp.FindAllString(line, -1)
	} else {
		commands = append(commands, line)
	}

	for i, command := range commands {
		commands[i] = strings.TrimSpace(command)
	}

	return
}

func parseCommand(line string) (name string, args []string) {
	regexpBySpace := regexp.MustCompile("\\s+")
	cmd := regexpBySpace.Split(line, -1)

	name = cmd[0]
	// expand environment variables
	// somehow os/exec.Command.Run() doesn't expand automatically
	envVarRegexp := regexp.MustCompile("^\\$(.+)$")
	for _, arg := range cmd[1:] {
		if envVarRegexp.MatchString(arg) {
			match := envVarRegexp.FindStringSubmatch(arg)
			arg = os.Getenv(match[1])
		}

		args = append(args, arg)
	}

	return
}

func isBuiltin(name string) bool {
	_, ok := BUILTINS[name]

	return ok
}

func callBuiltin(name string, args []string) {
	builtin, _ := BUILTINS[name]
	builtin.Run(args)
}

func spawnProgram(name string, args []string, placeHolderOut io.WriteCloser, placeHolderIn io.ReadCloser) {
	cmdFullPath, err := exec.LookPath(name)
	if err != nil {
		fmt.Fprintf(os.Stderr, "osh: command not found: %s\n", name)
		return
	}

	c := exec.Command(cmdFullPath, args...)
	c.Env = os.Environ()
	c.Stdin = placeHolderIn
	c.Stdout = placeHolderOut
	c.Stderr = c.Stdout

	if err = c.Run(); err != nil {
		panic(err)
	}
}

func spawnPrograms(cmds ...*exec.Cmd) {
	stdout, stderr, err := pipeline(cmds)
	if err != nil {
		fmt.Printf("%s\n", err)
	}

	if len(stdout) > 0 {
		fmt.Printf("%s", stdout)
	}

	if len(stderr) > 0 {
		fmt.Printf("%s", stderr)
	}
}

func pipeline(cmds []*exec.Cmd) (pipeLineOutput, collectedStandardError []byte, pipeLineError error) {
	// Collect the output from the command(s)
	var output bytes.Buffer
	var stderr bytes.Buffer

	last := len(cmds) - 1
	for i, cmd := range cmds[:last] {
		var err error
		// Connect each command's stdin to the previous command's stdout
		if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil {
			return nil, nil, err
		}
		// Connect each command's stderr to a buffer
		cmd.Stderr = &stderr
	}

	// Connect the output and error for the last command
	cmds[last].Stdout, cmds[last].Stderr = &output, &stderr

	// Start each command
	for _, cmd := range cmds {
		if err := cmd.Start(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Wait for each command to complete
	for _, cmd := range cmds {
		if err := cmd.Wait(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Return the pipeline output and the collected standard error
	return output.Bytes(), stderr.Bytes(), nil
}
<commit_msg>Remove unused method<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

type Builtin struct {
	Run func(args []string)
}

var BUILTINS = map[string]*Builtin{
	"cd": &Builtin{func(args []string) {
		os.Chdir(args[0])
	}},
	"exit": &Builtin{func(args []string) {
		var code int
		if len(args) == 1 {
			code, _ = strconv.Atoi(args[0])
		}
		os.Exit(code)
	}},
	"exec": &Builtin{func(args []string) {
		cmd := exec.Command(args[0], args[1:]...)
		spawnPrograms(cmd)
	}},
	"set": &Builtin{func(args []string) {
		for _, arg := range args {
			keyValuePair := strings.Split(arg, "=")
			if len(keyValuePair) == 2 {
				os.Setenv(keyValuePair[0], keyValuePair[1])
			}
		}
	}},
}

func init() {
	os.Setenv("PROMPT", "->")
}

func main() {
	prompt()

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		commands := splitOnPipes(scanner.Text())

		var cmds []*exec.Cmd
		for _, command := range commands {
			name, args := parseCommand(command)
			if name == "" {
				continue
			}

			if isBuiltin(name) {
				callBuiltin(name, args)
			} else {
				cmd := exec.Command(name, args...)
				cmds = append(cmds, cmd)
			}
		}

		if len(cmds) > 0 {
			spawnPrograms(cmds...)
		}

		prompt()
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "Reading standard input:", err)
	}
}

func prompt() {
	fmt.Fprintf(os.Stdout, "%s ", os.Getenv("PROMPT"))
}

func splitOnPipes(line string) (commands []string) {
	pipeRegexp := regexp.MustCompile("([^\"'|]+)|[\"']([^\"']+)[\"']")
	if pipeRegexp.MatchString(line) {
		commands = pipeRegexp.FindAllString(line, -1)
	} else {
		commands = append(commands, line)
	}

	for i, command := range commands {
		commands[i] = strings.TrimSpace(command)
	}

	return
}

func parseCommand(line string) (name string, args []string) {
	regexpBySpace := regexp.MustCompile("\\s+")
	cmd := regexpBySpace.Split(line, -1)

	name = cmd[0]
	// expand environment variables
	// somehow os/exec.Command.Run() doesn't expand automatically
	envVarRegexp := regexp.MustCompile("^\\$(.+)$")
	for _, arg := range cmd[1:] {
		if envVarRegexp.MatchString(arg) {
			match := envVarRegexp.FindStringSubmatch(arg)
			arg = os.Getenv(match[1])
		}

		args = append(args, arg)
	}

	return
}

func isBuiltin(name string) bool {
	_, ok := BUILTINS[name]

	return ok
}

func callBuiltin(name string, args []string) {
	builtin, _ := BUILTINS[name]
	builtin.Run(args)
}

func spawnPrograms(cmds ...*exec.Cmd) {
	stdout, stderr, err := pipeline(cmds)
	if err != nil {
		fmt.Printf("%s\n", err)
	}

	if len(stdout) > 0 {
		fmt.Printf("%s", stdout)
	}

	if len(stderr) > 0 {
		fmt.Printf("%s", stderr)
	}
}

func pipeline(cmds []*exec.Cmd) (pipeLineOutput, collectedStandardError []byte, pipeLineError error) {
	// Collect the output from the command(s)
	var output bytes.Buffer
	var stderr bytes.Buffer

	last := len(cmds) - 1
	for i, cmd := range cmds[:last] {
		var err error
		// Connect each command's stdin to the previous command's stdout
		if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil {
			return nil, nil, err
		}
		// Connect each command's stderr to a buffer
		cmd.Stderr = &stderr
	}

	// Connect the output and error for the last command
	cmds[last].Stdout, cmds[last].Stderr = &output, &stderr

	// Start each command
	for _, cmd := range cmds {
		if err := cmd.Start(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Wait for each command to complete
	for _, cmd := range cmds {
		if err := cmd.Wait(); err != nil {
			return output.Bytes(), stderr.Bytes(), err
		}
	}

	// Return the pipeline output and the collected standard error
	return output.Bytes(), stderr.Bytes(), nil
}
<|endoftext|>