<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/gopheracademy/congo/app"
	"github.com/gopheracademy/congo/js"
	"github.com/gopheracademy/congo/models"
	"github.com/gopheracademy/congo/swagger"
	"github.com/gopheracademy/congo/util"
	"github.com/jinzhu/gorm"
	"github.com/kelseyhightower/envconfig"
	_ "github.com/lib/pq"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/twitter"
	"github.com/raphael/goa"
	//	_ "gopkg.in/authboss.v0/remember"
	jg "github.com/dgrijalva/jwt-go"
	"github.com/raphael/goa-middleware/cors"
	"github.com/raphael/goa-middleware/jwt"
)

func main() {

	goth.UseProviders(
		twitter.New(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://arrakis:8080/api/auth/twitter/callback"),
		//		gplus.New(os.Getenv("GPLUS_KEY"), os.Getenv("GPLUS_SECRET"), "http://localhost:3000/auth/gplus/callback"),
		//		github.New(os.Getenv("GITHUB_KEY"), os.Getenv("GITHUB_SECRET"), "http://localhost:3000/auth/github/callback"),
		//		linkedin.New(os.Getenv("LINKEDIN_KEY"), os.Getenv("LINKEDIN_SECRET"), "http://localhost:3000/auth/linkedin/callback"),
	)
	cfg := util.Config{}
	if err := envconfig.Process("congo", &cfg); err != nil {
		log.Fatalf("config error [%s]", err)
		os.Exit(1)
	}

	// Create service
	service := goa.New("API")

	// Setup middleware
	service.Use(goa.RequestID())
	service.Use(goa.LogRequest())
	service.Use(goa.Recover())

	// cors specification
	cspec, err := cors.New(func() {
		cors.Origin("https://cfp.gophercon.com", func() {
			cors.Resource("/api", func() {
				cors.Headers("Authorization")
				cors.Methods("GET", "POST", "PUT", "DELETE", "OPTIONS")
				cors.MaxAge(600)
				cors.Credentials(true)
				cors.Vary("Http-Origin")
			})
		})
	})
	if err != nil {
		panic(err)
	}
	// mount the cors controller
	service.Use(cors.Middleware(cspec))

	db, err := connectDB()
	if err != nil {
		panic(err)
	}
	spec := &jwt.Specification{
		AllowParam:       false,
		AuthOptions:      false,
		TTLMinutes:       60,
		Issuer:           "api.gopheracademy.com",
		KeySigningMethod: jwt.RSA256,
		SigningKeyFunc:   privateKey,
		ValidationFunc:   pubKey,
	}

	tm := jwt.NewTokenManager(spec)
	claims := make(map[string]interface{})
	claims["sub"] = "1"

	t, err := tm.Create(claims)
	// a token to get in through the backdoor printed to standard out
	fmt.Println(t)

	a := NewAuthController(service, &db, tm, spec)
	app.MountAuthController(service, a)
	// Mount "user" controller
	c3 := NewUserController(service, models.NewUserDB(db))
	app.MountUserController(service, c3)
	c3.Use(jwt.Middleware(spec))
	c4 := NewProposalController(service, models.NewProposalDB(db))
	app.MountProposalController(service, c4)
	c4.Use(jwt.Middleware(spec))

	js.MountController(service)
	// Mount Swagger spec provider controller
	swagger.MountController(service)

	gothic.GetProviderName = provider

	hostStr := fmt.Sprintf(":%d", cfg.Port)
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}
	service.ServeFiles("/assets/public/*filepath", http.Dir(filepath.Join(dir, "assets", "public")))
	service.ListenAndServe(hostStr)
}
func provider(r *http.Request) (string, error) {
	return "twitter", nil
}
func connectDB() (gorm.DB, error) {
	var db gorm.DB
	constr := fmt.Sprintf("user=%s host=%s port=%d dbname=%s password=%s sslmode=disable",
		"postgres",
		"127.0.0.1",
		5433,
		"postgres",
		"postgres")

	var err error
	db, err = gorm.Open("postgres", constr)
	if err == nil {
		db.AutoMigrate(&models.User{})
	}
	db.LogMode(true)
	return db, err
}
func pubKey(*jg.Token) (interface{}, error) {
	return ioutil.ReadFile("keys/gc.rsa.pub")
}

func privateKey() (interface{}, error) {
	return ioutil.ReadFile("keys/gc.rsa")
}
<commit_msg>issue-18: update key names in main<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/gopheracademy/congo/app"
	"github.com/gopheracademy/congo/js"
	"github.com/gopheracademy/congo/models"
	"github.com/gopheracademy/congo/swagger"
	"github.com/gopheracademy/congo/util"
	"github.com/jinzhu/gorm"
	"github.com/kelseyhightower/envconfig"
	_ "github.com/lib/pq"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/twitter"
	"github.com/raphael/goa"
	//	_ "gopkg.in/authboss.v0/remember"
	jg "github.com/dgrijalva/jwt-go"
	"github.com/raphael/goa-middleware/cors"
	"github.com/raphael/goa-middleware/jwt"
)

func main() {

	goth.UseProviders(
		twitter.New(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://arrakis:8080/api/auth/twitter/callback"),
		//		gplus.New(os.Getenv("GPLUS_KEY"), os.Getenv("GPLUS_SECRET"), "http://localhost:3000/auth/gplus/callback"),
		//		github.New(os.Getenv("GITHUB_KEY"), os.Getenv("GITHUB_SECRET"), "http://localhost:3000/auth/github/callback"),
		//		linkedin.New(os.Getenv("LINKEDIN_KEY"), os.Getenv("LINKEDIN_SECRET"), "http://localhost:3000/auth/linkedin/callback"),
	)
	cfg := util.Config{}
	if err := envconfig.Process("congo", &cfg); err != nil {
		log.Fatalf("config error [%s]", err)
		os.Exit(1)
	}

	// Create service
	service := goa.New("API")

	// Setup middleware
	service.Use(goa.RequestID())
	service.Use(goa.LogRequest())
	service.Use(goa.Recover())

	// cors specification
	cspec, err := cors.New(func() {
		cors.Origin("https://cfp.gophercon.com", func() {
			cors.Resource("/api", func() {
				cors.Headers("Authorization")
				cors.Methods("GET", "POST", "PUT", "DELETE", "OPTIONS")
				cors.MaxAge(600)
				cors.Credentials(true)
				cors.Vary("Http-Origin")
			})
		})
	})
	if err != nil {
		panic(err)
	}
	// mount the cors controller
	service.Use(cors.Middleware(cspec))

	db, err := connectDB()
	if err != nil {
		panic(err)
	}
	spec := &jwt.Specification{
		AllowParam:       false,
		AuthOptions:      false,
		TTLMinutes:       60,
		Issuer:           "api.gopheracademy.com",
		KeySigningMethod: jwt.RSA256,
		SigningKeyFunc:   privateKey,
		ValidationFunc:   pubKey,
	}

	tm := jwt.NewTokenManager(spec)
	claims := make(map[string]interface{})
	claims["sub"] = "1"

	t, err := tm.Create(claims)
	// a token to get in through the backdoor printed to standard out
	fmt.Println(t)

	a := NewAuthController(service, &db, tm, spec)
	app.MountAuthController(service, a)
	// Mount "user" controller
	c3 := NewUserController(service, models.NewUserDB(db))
	app.MountUserController(service, c3)
	c3.Use(jwt.Middleware(spec))
	c4 := NewProposalController(service, models.NewProposalDB(db))
	app.MountProposalController(service, c4)
	c4.Use(jwt.Middleware(spec))

	js.MountController(service)
	// Mount Swagger spec provider controller
	swagger.MountController(service)

	gothic.GetProviderName = provider

	hostStr := fmt.Sprintf(":%d", cfg.Port)
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}
	service.ServeFiles("/assets/public/*filepath", http.Dir(filepath.Join(dir, "assets", "public")))
	service.ListenAndServe(hostStr)
}
func provider(r *http.Request) (string, error) {
	return "twitter", nil
}
func connectDB() (gorm.DB, error) {
	var db gorm.DB
	constr := fmt.Sprintf("user=%s host=%s port=%d dbname=%s password=%s sslmode=disable",
		"postgres",
		"127.0.0.1",
		5433,
		"postgres",
		"postgres")

	var err error
	db, err = gorm.Open("postgres", constr)
	if err == nil {
		db.AutoMigrate(&models.User{})
	}
	db.LogMode(true)
	return db, err
}
func pubKey(*jg.Token) (interface{}, error) {
	return ioutil.ReadFile("keys/congo.rsa.pub")
}

func privateKey() (interface{}, error) {
	return ioutil.ReadFile("keys/congo.rsa")
}
<|endoftext|>


<commit_before>package main

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

	"github.com/gophergala/go_report/check"
	"gopkg.in/mgo.v2"
	"labix.org/v2/mgo/bson"
)

var (
	mongoURL        = "mongodb://localhost:27017"
	mongoDatabase   = "goreportcard"
	mongoCollection = "reports"
)

func getMongoCollection() (*mgo.Collection, error) {
	session, err := mgo.Dial(mongoURL)
	if err != nil {
		return nil, err
	}
	c := session.DB(mongoDatabase).C(mongoCollection)
	return c, nil
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Serving home page")
	if r.URL.Path[1:] == "" {
		http.ServeFile(w, r, "templates/home.html")
	} else {
		http.NotFound(w, r)
	}
}

func assetsHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Serving " + r.URL.Path[1:])
	http.ServeFile(w, r, r.URL.Path[1:])
}

func orgRepoNames(url string) (string, string) {
	dir := strings.TrimSuffix(url, ".git")
	split := strings.Split(dir, "/")
	org := split[len(split)-2]
	repoName := split[len(split)-1]

	return org, repoName
}

func dirName(url string) string {
	org, repoName := orgRepoNames(url)

	return fmt.Sprintf("repos/src/github.com/%s/%s", org, repoName)
}

func clone(url string) error {
	org, _ := orgRepoNames(url)
	if err := os.Mkdir(fmt.Sprintf("repos/src/github.com/%s", org), 0755); err != nil && !os.IsExist(err) {
		return fmt.Errorf("could not create dir: %v", err)
	}
	dir := dirName(url)
	_, err := os.Stat(dir)
	if os.IsNotExist(err) {
		cmd := exec.Command("timeout", "git", "clone", "--depth", "1", "--single-branch", url, dir)
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("could not run git clone: %v", err)
		}
	} else if err != nil {
		return fmt.Errorf("could not stat dir: %v", err)
	} else {
		cmd := exec.Command("git", "-C", dir, "pull")
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("could not pull repo: %v", err)
		}
	}

	return nil
}

type score struct {
	Name          string              `json:"name"`
	Description   string              `json:"description"`
	FileSummaries []check.FileSummary `json:"file_summaries"`
	Percentage    float64             `json:"percentage"`
}

type checksResp struct {
	Checks      []score   `json:"checks"`
	Average     float64   `json:"average"`
	Files       int       `json:"files"`
	Issues      int       `json:"issues"`
	Repo        string    `json:"repo"`
	LastRefresh time.Time `json:"last_refresh"`
}

func getFromCache(repo string) (checksResp, error) {
	// try and fetch from mongo
	coll, err := getMongoCollection()
	if err != nil {
		return checksResp{}, fmt.Errorf("Failed to get mongo collection during GET: %v", err)
	}
	resp := checksResp{}
	err = coll.Find(bson.M{"repo": repo}).One(&resp)
	if err != nil {
		return checksResp{}, fmt.Errorf("Failed to fetch %q from mongo: %v", repo, err)
	}

	resp.LastRefresh = resp.LastRefresh.UTC()

	return resp, nil
}

func checkHandler(w http.ResponseWriter, r *http.Request) {
	repo := r.FormValue("repo")
	url := repo
	if !strings.HasPrefix(url, "https://github.com/") {
		url = "https://github.com/" + url
	}

	w.Header().Set("Content-Type", "application/json")

	// if this is a GET request, fetch from cached version in mongo
	if r.Method == "GET" {
		resp, err := getFromCache(repo)
		if err != nil {
			// just log the error and continue
			log.Println(err)
		} else {
			b, err := json.Marshal(resp)
			if err != nil {
				log.Println("ERROR: could not marshal json:", err)
				http.Error(w, err.Error(), 500)
				return
			}
			w.Write(b)
			log.Println("Loaded from cache!", repo)
			return
		}
	}

	err := clone(url)
	if err != nil {
		log.Println("ERROR: could not clone repo: ", err)
		http.Error(w, fmt.Sprintf("Could not clone repo: %v", err), 500)
		return
	}

	dir := dirName(url)
	filenames, err := check.GoFiles(dir)
	if err != nil {
		log.Println("ERROR: could not get filenames: ", err)
		http.Error(w, fmt.Sprintf("Could not get filenames: %v", err), 500)
		return
	}
	checks := []check.Check{check.GoFmt{Dir: dir, Filenames: filenames},
		check.GoVet{Dir: dir, Filenames: filenames},
		check.GoLint{Dir: dir, Filenames: filenames},
		check.GoCyclo{Dir: dir, Filenames: filenames},
	}

	ch := make(chan score)
	for _, c := range checks {
		go func(c check.Check) {
			p, summaries, err := c.Percentage()
			if err != nil {
				log.Printf("ERROR: (%s) %v", c.Name(), err)
			}
			s := score{
				Name:          c.Name(),
				Description:   c.Description(),
				FileSummaries: summaries,
				Percentage:    p,
			}
			ch <- s
		}(c)
	}

	resp := checksResp{Repo: repo,
		Files:       len(filenames),
		LastRefresh: time.Now().UTC()}
	var avg float64
	var issues = make(map[string]bool)
	for i := 0; i < len(checks); i++ {
		s := <-ch
		resp.Checks = append(resp.Checks, s)
		avg += s.Percentage
		for _, fs := range s.FileSummaries {
			issues[fs.Filename] = true
		}
	}

	resp.Average = avg / float64(len(checks))
	resp.Issues = len(issues)

	b, err := json.Marshal(resp)
	if err != nil {
		log.Println("ERROR: could not marshal json:", err)
		http.Error(w, err.Error(), 500)
		return
	}
	w.Write(b)

	// write to mongo
	coll, err := getMongoCollection()
	if err != nil {
		log.Println("Failed to get mongo collection: ", err)
	} else {
		log.Println("Writing to mongo...")
		_, err := coll.Upsert(bson.M{"Repo": repo}, resp)
		if err != nil {
			log.Println("Mongo writing error:", err)
		}
	}
}

func reportHandler(w http.ResponseWriter, r *http.Request, org, repo string) {
	http.ServeFile(w, r, "templates/home.html")
}

func makeReportHandler(fn func(http.ResponseWriter, *http.Request, string, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		validPath := regexp.MustCompile(`^/report/([a-zA-Z0-9\-_]+)/([a-zA-Z0-9\-_]+)$`)

		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[1], m[2])
	}
}

func main() {
	if err := os.MkdirAll("repos/src/github.com", 0755); err != nil && !os.IsExist(err) {
		log.Fatal("ERROR: could not create repos dir: ", err)
	}

	http.HandleFunc("/assets/", assetsHandler)
	http.HandleFunc("/checks", checkHandler)
	http.HandleFunc("/report/", makeReportHandler(reportHandler))
	http.HandleFunc("/", homeHandler)

	fmt.Println("Running on 127.0.01:8080...")
	log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
}
<commit_msg>add 120 to timeout<commit_after>package main

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

	"github.com/gophergala/go_report/check"
	"gopkg.in/mgo.v2"
	"labix.org/v2/mgo/bson"
)

var (
	mongoURL        = "mongodb://localhost:27017"
	mongoDatabase   = "goreportcard"
	mongoCollection = "reports"
)

func getMongoCollection() (*mgo.Collection, error) {
	session, err := mgo.Dial(mongoURL)
	if err != nil {
		return nil, err
	}
	c := session.DB(mongoDatabase).C(mongoCollection)
	return c, nil
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Serving home page")
	if r.URL.Path[1:] == "" {
		http.ServeFile(w, r, "templates/home.html")
	} else {
		http.NotFound(w, r)
	}
}

func assetsHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Serving " + r.URL.Path[1:])
	http.ServeFile(w, r, r.URL.Path[1:])
}

func orgRepoNames(url string) (string, string) {
	dir := strings.TrimSuffix(url, ".git")
	split := strings.Split(dir, "/")
	org := split[len(split)-2]
	repoName := split[len(split)-1]

	return org, repoName
}

func dirName(url string) string {
	org, repoName := orgRepoNames(url)

	return fmt.Sprintf("repos/src/github.com/%s/%s", org, repoName)
}

func clone(url string) error {
	org, _ := orgRepoNames(url)
	if err := os.Mkdir(fmt.Sprintf("repos/src/github.com/%s", org), 0755); err != nil && !os.IsExist(err) {
		return fmt.Errorf("could not create dir: %v", err)
	}
	dir := dirName(url)
	_, err := os.Stat(dir)
	if os.IsNotExist(err) {
		cmd := exec.Command("timeout", "120", "git", "clone", "--depth", "1", "--single-branch", url, dir)
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("could not run git clone: %v", err)
		}
	} else if err != nil {
		return fmt.Errorf("could not stat dir: %v", err)
	} else {
		cmd := exec.Command("git", "-C", dir, "pull")
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("could not pull repo: %v", err)
		}
	}

	return nil
}

type score struct {
	Name          string              `json:"name"`
	Description   string              `json:"description"`
	FileSummaries []check.FileSummary `json:"file_summaries"`
	Percentage    float64             `json:"percentage"`
}

type checksResp struct {
	Checks      []score   `json:"checks"`
	Average     float64   `json:"average"`
	Files       int       `json:"files"`
	Issues      int       `json:"issues"`
	Repo        string    `json:"repo"`
	LastRefresh time.Time `json:"last_refresh"`
}

func getFromCache(repo string) (checksResp, error) {
	// try and fetch from mongo
	coll, err := getMongoCollection()
	if err != nil {
		return checksResp{}, fmt.Errorf("Failed to get mongo collection during GET: %v", err)
	}
	resp := checksResp{}
	err = coll.Find(bson.M{"repo": repo}).One(&resp)
	if err != nil {
		return checksResp{}, fmt.Errorf("Failed to fetch %q from mongo: %v", repo, err)
	}

	resp.LastRefresh = resp.LastRefresh.UTC()

	return resp, nil
}

func checkHandler(w http.ResponseWriter, r *http.Request) {
	repo := r.FormValue("repo")
	url := repo
	if !strings.HasPrefix(url, "https://github.com/") {
		url = "https://github.com/" + url
	}

	w.Header().Set("Content-Type", "application/json")

	// if this is a GET request, fetch from cached version in mongo
	if r.Method == "GET" {
		resp, err := getFromCache(repo)
		if err != nil {
			// just log the error and continue
			log.Println(err)
		} else {
			b, err := json.Marshal(resp)
			if err != nil {
				log.Println("ERROR: could not marshal json:", err)
				http.Error(w, err.Error(), 500)
				return
			}
			w.Write(b)
			log.Println("Loaded from cache!", repo)
			return
		}
	}

	err := clone(url)
	if err != nil {
		log.Println("ERROR: could not clone repo: ", err)
		http.Error(w, fmt.Sprintf("Could not clone repo: %v", err), 500)
		return
	}

	dir := dirName(url)
	filenames, err := check.GoFiles(dir)
	if err != nil {
		log.Println("ERROR: could not get filenames: ", err)
		http.Error(w, fmt.Sprintf("Could not get filenames: %v", err), 500)
		return
	}
	checks := []check.Check{check.GoFmt{Dir: dir, Filenames: filenames},
		check.GoVet{Dir: dir, Filenames: filenames},
		check.GoLint{Dir: dir, Filenames: filenames},
		check.GoCyclo{Dir: dir, Filenames: filenames},
	}

	ch := make(chan score)
	for _, c := range checks {
		go func(c check.Check) {
			p, summaries, err := c.Percentage()
			if err != nil {
				log.Printf("ERROR: (%s) %v", c.Name(), err)
			}
			s := score{
				Name:          c.Name(),
				Description:   c.Description(),
				FileSummaries: summaries,
				Percentage:    p,
			}
			ch <- s
		}(c)
	}

	resp := checksResp{Repo: repo,
		Files:       len(filenames),
		LastRefresh: time.Now().UTC()}
	var avg float64
	var issues = make(map[string]bool)
	for i := 0; i < len(checks); i++ {
		s := <-ch
		resp.Checks = append(resp.Checks, s)
		avg += s.Percentage
		for _, fs := range s.FileSummaries {
			issues[fs.Filename] = true
		}
	}

	resp.Average = avg / float64(len(checks))
	resp.Issues = len(issues)

	b, err := json.Marshal(resp)
	if err != nil {
		log.Println("ERROR: could not marshal json:", err)
		http.Error(w, err.Error(), 500)
		return
	}
	w.Write(b)

	// write to mongo
	coll, err := getMongoCollection()
	if err != nil {
		log.Println("Failed to get mongo collection: ", err)
	} else {
		log.Println("Writing to mongo...")
		_, err := coll.Upsert(bson.M{"Repo": repo}, resp)
		if err != nil {
			log.Println("Mongo writing error:", err)
		}
	}
}

func reportHandler(w http.ResponseWriter, r *http.Request, org, repo string) {
	http.ServeFile(w, r, "templates/home.html")
}

func makeReportHandler(fn func(http.ResponseWriter, *http.Request, string, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		validPath := regexp.MustCompile(`^/report/([a-zA-Z0-9\-_]+)/([a-zA-Z0-9\-_]+)$`)

		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[1], m[2])
	}
}

func main() {
	if err := os.MkdirAll("repos/src/github.com", 0755); err != nil && !os.IsExist(err) {
		log.Fatal("ERROR: could not create repos dir: ", err)
	}

	http.HandleFunc("/assets/", assetsHandler)
	http.HandleFunc("/checks", checkHandler)
	http.HandleFunc("/report/", makeReportHandler(reportHandler))
	http.HandleFunc("/", homeHandler)

	fmt.Println("Running on 127.0.01:8080...")
	log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"

	"github.com/asim/mq/go/client"
	"github.com/gorilla/handlers"
	"github.com/gorilla/websocket"
)

type mq struct {
	client client.Client

	sync.RWMutex
	topics map[string][]chan []byte
}

var (
	address = flag.String("address", ":8081", "MQ server address")
	cert    = flag.String("cert_file", "", "TLS certificate file")
	key     = flag.String("key_file", "", "TLS key file")
	proxy   = flag.Bool("proxy", false, "Proxy for an MQ cluster")
	servers = flag.String("servers", "", "Comma separated MQ cluster list used by Proxy")

	defaultMQ *mq
)

func init() {
	flag.Parse()

	if *proxy && len(*servers) == 0 {
		log.Fatal("Proxy enabled without MQ server list")
	}

	defaultMQ = &mq{
		client: client.New(client.WithServers(strings.Split(*servers, ",")...)),
		topics: make(map[string][]chan []byte),
	}
}

func (m *mq) pub(topic string, payload []byte) error {
	if *proxy {
		return m.client.Publish(topic, payload)
	}

	m.RLock()
	subscribers, ok := m.topics[topic]
	m.RUnlock()
	if !ok {
		return nil
	}

	go func() {
		for _, subscriber := range subscribers {
			select {
			case subscriber <- payload:
			default:
			}
		}
	}()

	return nil
}

func (m *mq) sub(topic string) (<-chan []byte, error) {
	if *proxy {
		return m.client.Subscribe(topic)
	}

	ch := make(chan []byte, 100)
	m.Lock()
	m.topics[topic] = append(m.topics[topic], ch)
	m.Unlock()
	return ch, nil
}

func (m *mq) unsub(topic string, sub <-chan []byte) error {
	if *proxy {
		return m.client.Unsubscribe(sub)
	}

	m.RLock()
	subscribers, ok := m.topics[topic]
	m.RUnlock()

	if !ok {
		return nil
	}

	var subs []chan []byte
	for _, subscriber := range subscribers {
		if subscriber == sub {
			continue
		}
		subs = append(subs, subscriber)
	}

	m.Lock()
	m.topics[topic] = subs
	m.Unlock()

	return nil
}

func pub(w http.ResponseWriter, r *http.Request) {
	topic := r.URL.Query().Get("topic")
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Pub error", http.StatusInternalServerError)
		return
	}
	r.Body.Close()

	err = defaultMQ.pub(topic, b)
	if err != nil {
		http.Error(w, "Pub error", http.StatusInternalServerError)
		return
	}
}

func sub(w http.ResponseWriter, r *http.Request) {
	conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
	if err != nil {
		log.Println("Failed to open websocket connection")
		http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
		return
	}

	topic := r.URL.Query().Get("topic")
	ch, err := defaultMQ.sub(topic)
	if err != nil {
		log.Printf("Failed to retrieve event for %s topic", topic)
		http.Error(w, "Could not retrieve events", http.StatusInternalServerError)
		return
	}
	defer defaultMQ.unsub(topic, ch)

	for {
		select {
		case e := <-ch:
			if err = conn.WriteMessage(websocket.BinaryMessage, e); err != nil {
				log.Printf("error sending event: %v", err.Error())
				return
			}
		}
	}
}

func main() {
	// MQ Handlers
	http.HandleFunc("/pub", pub)
	http.HandleFunc("/sub", sub)

	// logging handler
	handler := handlers.LoggingHandler(os.Stdout, http.DefaultServeMux)

	if len(*cert) > 0 && len(*key) > 0 {
		log.Println("TLS Enabled")
		log.Println("MQ listening on", *address)
		http.ListenAndServeTLS(*address, *cert, *key, handler)
		return
	}

	if *proxy {
		log.Println("Proxy enabled")
	}

	log.Println("MQ listening on", *address)
	http.ListenAndServe(*address, handler)
}
<commit_msg>strip logging<commit_after>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"

	"github.com/asim/mq/go/client"
	"github.com/gorilla/handlers"
	"github.com/gorilla/websocket"
)

type mq struct {
	client client.Client

	sync.RWMutex
	topics map[string][]chan []byte
}

var (
	address = flag.String("address", ":8081", "MQ server address")
	cert    = flag.String("cert_file", "", "TLS certificate file")
	key     = flag.String("key_file", "", "TLS key file")
	proxy   = flag.Bool("proxy", false, "Proxy for an MQ cluster")
	servers = flag.String("servers", "", "Comma separated MQ cluster list used by Proxy")

	defaultMQ *mq
)

func init() {
	flag.Parse()

	if *proxy && len(*servers) == 0 {
		log.Fatal("Proxy enabled without MQ server list")
	}

	defaultMQ = &mq{
		client: client.New(client.WithServers(strings.Split(*servers, ",")...)),
		topics: make(map[string][]chan []byte),
	}
}

func (m *mq) pub(topic string, payload []byte) error {
	if *proxy {
		return m.client.Publish(topic, payload)
	}

	m.RLock()
	subscribers, ok := m.topics[topic]
	m.RUnlock()
	if !ok {
		return nil
	}

	go func() {
		for _, subscriber := range subscribers {
			select {
			case subscriber <- payload:
			default:
			}
		}
	}()

	return nil
}

func (m *mq) sub(topic string) (<-chan []byte, error) {
	if *proxy {
		return m.client.Subscribe(topic)
	}

	ch := make(chan []byte, 100)
	m.Lock()
	m.topics[topic] = append(m.topics[topic], ch)
	m.Unlock()
	return ch, nil
}

func (m *mq) unsub(topic string, sub <-chan []byte) error {
	if *proxy {
		return m.client.Unsubscribe(sub)
	}

	m.RLock()
	subscribers, ok := m.topics[topic]
	m.RUnlock()

	if !ok {
		return nil
	}

	var subs []chan []byte
	for _, subscriber := range subscribers {
		if subscriber == sub {
			continue
		}
		subs = append(subs, subscriber)
	}

	m.Lock()
	m.topics[topic] = subs
	m.Unlock()

	return nil
}

func pub(w http.ResponseWriter, r *http.Request) {
	topic := r.URL.Query().Get("topic")
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Pub error", http.StatusInternalServerError)
		return
	}
	r.Body.Close()

	err = defaultMQ.pub(topic, b)
	if err != nil {
		http.Error(w, "Pub error", http.StatusInternalServerError)
		return
	}
}

func sub(w http.ResponseWriter, r *http.Request) {
	conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
	if err != nil {
		http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
		return
	}

	topic := r.URL.Query().Get("topic")
	ch, err := defaultMQ.sub(topic)
	if err != nil {
		http.Error(w, "Could not retrieve events", http.StatusInternalServerError)
		return
	}
	defer defaultMQ.unsub(topic, ch)

	for {
		select {
		case e := <-ch:
			if err = conn.WriteMessage(websocket.BinaryMessage, e); err != nil {
				return
			}
		}
	}
}

func main() {
	// MQ Handlers
	http.HandleFunc("/pub", pub)
	http.HandleFunc("/sub", sub)

	// logging handler
	handler := handlers.LoggingHandler(os.Stdout, http.DefaultServeMux)

	if len(*cert) > 0 && len(*key) > 0 {
		log.Println("TLS Enabled")
		log.Println("MQ listening on", *address)
		http.ListenAndServeTLS(*address, *cert, *key, handler)
		return
	}

	if *proxy {
		log.Println("Proxy enabled")
	}

	log.Println("MQ listening on", *address)
	http.ListenAndServe(*address, handler)
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

// 也可以以服务的方式启动nging
// 服务支持的操作有：
// nging service install  	-- 安装服务
// nging service uninstall  -- 卸载服务
// nging service start 		-- 启动服务
// nging service stop 		-- 停止服务
// nging service restart 	-- 重启服务
package main

//go:generate go get github.com/admpub/bindata/v3/...
//go:generate go-bindata -fs -o bindata_assetfs.go -ignore "\\.(git|svn|DS_Store|less|scss)$" -minify "\\.(js|css)$" -tags bindata public/assets/... template/... config/i18n/...

import (
	_ "github.com/admpub/bindata/v3"
	"github.com/admpub/log"
	"github.com/admpub/nging/application/cmd"
	_ "github.com/admpub/xgo"
	"github.com/webx-top/echo"

	//register

	_ "github.com/admpub/nging/application"
	_ "github.com/admpub/nging/application/initialize/manager"
	_ "github.com/admpub/nging/application/library/sqlite"
)

var (
	BUILD_TIME string
	CLOUD_GOX  string
	COMMIT     string
	LABEL      = `dev` //beta/alpha/stable
	VERSION    = `2.2.5`

	version   string
	schemaVer = 3.6 //数据表结构版本
)

func main() {
	defer log.Sync()
	echo.Set(`BUILD_TIME`, BUILD_TIME)
	echo.Set(`COMMIT`, COMMIT)
	echo.Set(`LABEL`, LABEL)
	echo.Set(`VERSION`, VERSION)
	echo.Set(`SCHEMA_VER`, schemaVer)
	exec()
}

func exec() {
	cmd.Execute()
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

// 也可以以服务的方式启动nging
// 服务支持的操作有：
// nging service install  	-- 安装服务
// nging service uninstall  -- 卸载服务
// nging service start 		-- 启动服务
// nging service stop 		-- 停止服务
// nging service restart 	-- 重启服务
package main

//go:generate go get github.com/admpub/bindata/v3/...
//go:generate go-bindata -fs -o bindata_assetfs.go -ignore "\\.(git|svn|DS_Store|less|scss)$" -minify "\\.(js|css)$" -tags bindata public/assets/... template/... config/i18n/...

import (
	_ "github.com/admpub/bindata/v3"
	"github.com/admpub/log"
	"github.com/admpub/nging/application/cmd"
	"github.com/webx-top/echo"

	//register

	_ "github.com/admpub/nging/application"
	_ "github.com/admpub/nging/application/initialize/manager"
	_ "github.com/admpub/nging/application/library/sqlite"
)

var (
	BUILD_TIME string
	CLOUD_GOX  string
	COMMIT     string
	LABEL      = `dev` //beta/alpha/stable
	VERSION    = `2.2.5`

	version   string
	schemaVer = 3.6 //数据表结构版本
)

func main() {
	defer log.Sync()
	echo.Set(`BUILD_TIME`, BUILD_TIME)
	echo.Set(`COMMIT`, COMMIT)
	echo.Set(`LABEL`, LABEL)
	echo.Set(`VERSION`, VERSION)
	echo.Set(`SCHEMA_VER`, schemaVer)
	exec()
}

func exec() {
	cmd.Execute()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net"
	"os"
	"strconv"
	"strings"

	//"github.com/hashicorp/serf/client"
	"github.com/hashicorp/serf/command"
	"github.com/hashicorp/serf/command/agent"
	"github.com/hashicorp/serf/serf"
)

var (
	c                          = serf.DefaultConfig()
	mode           instaceMode = SlaveMode
	masterAddress  string
	listenAddress  string // this is the interface that serf runs on
	rpcAddress     string // this is the interface that serf's RPC runs on (localhost:7373)
	defaultName, _ = os.Hostname()
	rpcAuthKey     = ""
)

type instaceMode int

const (
	MasterMode instaceMode = iota
	SlaveMode
	DeployMode
)

func init() {
	var name string
	var tagsFile string
	flag.StringVar(&masterAddress, "master", "localhost:7946", "Join the cluster by coordinating with this master")
	flag.StringVar(&listenAddress, "listen", "localhost:7946", "Listen on the address for serf communication")
	flag.StringVar(&rpcAddress, "rpc", "localhost:7373", "RPC address of the serfbort master")
	flag.StringVar(&name, "name", defaultName, "Name to use in serf protocol")
	flag.StringVar(&tagsFile, "tags-file", "", "Load tags for agent from file (json format)")
	flag.Parse()

	if tagsFile != "" {
		tags, err := loadTagsFromFile(tagsFile)
		if err != nil {
			log.Fatalf("Unable to load tags from file: %s", err)
		}
		c.Tags = tags
	}
	c.NodeName = name

}

func main() {
	fields := strings.Split(listenAddress, ":")
	if len(fields) != 2 {
		log.Fatalf("-listen requires host:port! %s is not valid", listenAddress)
	}
	bindAddr := fields[0]
	bindPort, err := strconv.Atoi(fields[1])
	if err != nil {
		log.Fatalf("Unable to parse %s into port", fields[1])
	}
	c.MemberlistConfig.BindAddr = bindAddr
	c.MemberlistConfig.BindPort = bindPort
	//TODO advertiseaddr and advertiseport
	//c.MemberlistConfig.AdvertiseAddr = x
	//c.MemberlistConfig.AdvertisePort = y

	evtCh := make(chan serf.Event, 64)
	c.EventCh = evtCh

	args := flag.Args()
	if len(args) > 0 {
		switch args[0] {
		case "master":
			mode = MasterMode
		case "deploy":
			mode = DeployMode
		default:
			mode = SlaveMode
		}
	}

	switch mode {
	case SlaveMode:
		log.Printf("Starting agent with tags %v", c.Tags)
		//TODO pull this into the Agent type
		s, err := serf.Create(c)
		if err != nil {
			log.Fatalf("Error creating Serf: %s", err)
		}

		log.Printf("Joining cluster by way of %s", masterAddress)
		n, err := s.Join([]string{masterAddress}, true)
		if n > 0 {
			log.Printf("Cluster joined; %d nodes participating", n)
		}
		if err != nil {
			log.Fatalf("unable to join cluster: %s", err)
		}

		a := AgentEventHandler{s, evtCh}
		a.EventLoop()

	case MasterMode:
		log.Printf("Starting master on %s", listenAddress)
		log.Printf("Starting master RPC listener on %s", rpcAddress)
		//TODO pull this crap into the Master type
		logOutput := os.Stderr
		logWriter := agent.NewLogWriter(123)
		a, err := agent.Create(agent.DefaultConfig(), c, logWriter)
		if err != nil {
			log.Fatalf("Unable to create agent: %s", err)
		}

		//register event handlers
		a.RegisterEventHandler(MasterEventHandler{})

		if err := a.Start(); err != nil {
			log.Fatalf("Unable to start agent: %s", err)
		}

		log.Printf("Joining cluster by way of %s", masterAddress)
		n, err := a.Join([]string{masterAddress}, true)
		if n > 0 {
			log.Printf("Cluster joined; %d nodes participating", n)
		}
		if err != nil {
			log.Fatalf("unable to join cluster: %s", err)
		}

		members := a.Serf().Members()
		log.Printf("%d nodes currently in cluster:", len(members))
		for _, m := range members {
			log.Printf("  %s %s:%d %v %s", m.Name, m.Addr, m.Port, m.Tags, m.Status)
		}

		//TODO we should create an agent with agent.Create instead of this!
		//a := agent.Agent{}
		rpcListener, err := net.Listen("tcp", rpcAddress)
		if err != nil {
			log.Fatal("Error starting RPC listener: %s", err)
		}
		agent.NewAgentIPC(a, rpcAuthKey, rpcListener, logOutput, logWriter)
		select {}

	case DeployMode:
		rpcclient, err := command.RPCClient(rpcAddress, rpcAuthKey)
		if err != nil {
			log.Fatalf("Unable to connect to master at %s: %s", rpcAddress, err)
		}

		cmd := "deploy"
		payload := []byte("fuck")
		log.Printf("Sending %s command with payload %q", cmd, payload)
		err = rpcclient.UserEvent(cmd, payload, false)
		if err != nil {
			log.Fatal(err)
		}
		log.Print("OK")
	}

}

func leaveCluster() {
	//TODO this defer isnt running when ctrl-c'd
	/*
			go func() {
		    sig := <-sigChan
		        log.Printf("Cleaning up registered callback after signal %s\n", sig)
		                              os.Exit(0)
				log.Print("fuck")
				if s != nil {
					log.Print("Leaving cluster")
					err := s.Leave()
					if err != nil {
						log.Print(err)
						return
					}
					log.Print("Shutting down")
					err = s.Shutdown()
					if err != nil {
						log.Print(err)
						return
					}

				}
			}()
	*/
}
<commit_msg>refactor CLI and make it not suck<commit_after>package main

import (
	"log"
	"net"
	"os"
	"strconv"
	"strings"

	//"github.com/hashicorp/serf/client"
	"github.com/codegangsta/cli"
	"github.com/hashicorp/serf/command"
	"github.com/hashicorp/serf/command/agent"
	"github.com/hashicorp/serf/serf"
)

var (
	defaultName, _ = os.Hostname()
)

func main() {

	app := cli.NewApp()
	app.Name = "serfbort"
	app.Usage = "deploy tool"
	app.Action = cli.ShowAppHelp
	//top level flags, common to all commands
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "rpc",
			Value: "localhost:7373",
			Usage: "Listen on the address for RPC requests (i.e. from deploy command)",
		},
		cli.StringFlag{
			Name:  "rpc-auth",
			Usage: "Auth token to use for RPC",
		},
		cli.StringFlag{
			Name:   "config",
			Usage:  "JSON config describing deploy targets, commands, etc",
			EnvVar: "SERFBORT_CONFIG",
		},
	}
	// subcommands
	app.Commands = []cli.Command{
		{
			Name: "master",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "name",
					Value: defaultName,
					Usage: "Name to use in serf protocol",
				},
				cli.StringFlag{
					Name:  "master",
					Value: "localhost:7946",
					Usage: "Join the cluster by coordinating with this master",
				},
				cli.StringFlag{
					Name:  "listen",
					Value: "localhost:7946",
					Usage: "Listen on the address for serf communication",
				},
			},
			Usage:  "Run the serfbort master process",
			Action: StartMaster,
		},
		{
			Name: "deploy",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "verify, v",
					Usage: "Run verification of the deploy",
				},
			},
			//TODO add subcommands dynamically based on a config?
			Subcommands: []cli.Command{},
			Usage:       "Perform a deploy",
			Action:      DoDeploy,
		},
		{
			Name: "agent",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "master",
					Value: "localhost:7946",
					Usage: "Join the cluster by coordinating with this master",
				},
				cli.StringFlag{
					Name:  "listen",
					Value: "localhost:7947",
					Usage: "Listen on the address for serf communication",
				},
				cli.StringFlag{
					Name:  "name",
					Value: defaultName,
					Usage: "Name to use in serf protocol",
				},
				cli.StringFlag{
					Name:  "tags-file",
					Usage: "Load tags for agent from file (json format)",
				},
			},
			Usage:  "Run the serfbort agent",
			Action: StartAgent,
		},
	}

	app.Run(os.Args)

}

func StartAgent(c *cli.Context) {
	//TODO make this work with authkeys
	//rpcAddress := c.GlobalString("rpc")
	//rpcAuthKey := c.GlobalString("rpc-auth")
	masterAddress := c.String("master")
	listenAddress := c.String("listen")

	serfConfig := serf.DefaultConfig()
	fields := strings.Split(listenAddress, ":")
	if len(fields) != 2 {
		log.Fatalf("listen requires host:port! %s is not valid", listenAddress)
	}
	bindAddr := fields[0]
	bindPort, err := strconv.Atoi(fields[1])
	if err != nil {
		log.Fatalf("Unable to parse %s into port", fields[1])
	}
	serfConfig.MemberlistConfig.BindAddr = bindAddr
	serfConfig.MemberlistConfig.BindPort = bindPort

	evtCh := make(chan serf.Event, 64)
	serfConfig.EventCh = evtCh

	if c.IsSet("tags-file") {
		tagsLoaded, err := loadTagsFromFile(c.String("tags-file"))
		if err != nil {
			log.Fatalf("Unable to load tags from file: %s", err)
		}
		serfConfig.Tags = tagsLoaded
	}
	if c.IsSet("name") {
		serfConfig.NodeName = c.String("name")
	}
	log.Printf("Starting agent with tags %v", serfConfig.Tags)
	//TODO pull this into the Agent type
	s, err := serf.Create(serfConfig)
	if err != nil {
		log.Fatalf("Error creating Serf: %s", err)
	}

	log.Printf("Joining cluster by way of %s", masterAddress)
	n, err := s.Join([]string{masterAddress}, true)
	if n > 0 {
		log.Printf("Cluster joined; %d nodes participating", n)
	}
	if err != nil {
		log.Fatalf("unable to join cluster: %s", err)
	}

	a := AgentEventHandler{s, evtCh}
	a.EventLoop()
}

func StartMaster(c *cli.Context) {
	rpcAddress := c.GlobalString("rpc")
	rpcAuthKey := c.GlobalString("rpc-auth")
	masterAddress := c.String("master")
	listenAddress := c.String("listen")

	fields := strings.Split(listenAddress, ":")
	if len(fields) != 2 {
		log.Fatalf("listen requires host:port! %s is not valid", listenAddress)
	}
	bindAddr := fields[0]
	bindPort, err := strconv.Atoi(fields[1])
	if err != nil {
		log.Fatalf("Unable to parse %s into port", fields[1])
	}
	serfConfig := serf.DefaultConfig()
	serfConfig.MemberlistConfig.BindAddr = bindAddr
	serfConfig.MemberlistConfig.BindPort = bindPort
	if c.IsSet("name") {
		serfConfig.NodeName = c.String("name")
	}

	log.Printf("Starting master on %s", listenAddress)
	log.Printf("Starting master RPC listener on %s", rpcAddress)
	//TODO pull this crap into the Master type
	logOutput := os.Stderr
	logWriter := agent.NewLogWriter(123)
	a, err := agent.Create(agent.DefaultConfig(), serfConfig, logWriter)
	if err != nil {
		log.Fatalf("Unable to create agent: %s", err)
	}

	//register event handlers
	a.RegisterEventHandler(MasterEventHandler{})

	if err := a.Start(); err != nil {
		log.Fatalf("Unable to start agent: %s", err)
	}

	log.Printf("Joining cluster by way of %s", masterAddress)
	n, err := a.Join([]string{masterAddress}, true)
	if n > 0 {
		log.Printf("Cluster joined; %d nodes participating", n)
	}
	if err != nil {
		log.Fatalf("unable to join cluster: %s", err)
	}

	members := a.Serf().Members()
	log.Printf("%d nodes currently in cluster:", len(members))
	for _, m := range members {
		log.Printf("  %s %s:%d %v %s", m.Name, m.Addr, m.Port, m.Tags, m.Status)
	}

	//TODO we should create an agent with agent.Create instead of this!
	//a := agent.Agent{}
	rpcListener, err := net.Listen("tcp", rpcAddress)
	if err != nil {
		log.Fatal("Error starting RPC listener: %s", err)
	}
	agent.NewAgentIPC(a, rpcAuthKey, rpcListener, logOutput, logWriter)
	select {}

}
func DoDeploy(c *cli.Context) {
	rpcAddress := c.GlobalString("rpc")
	rpcAuthKey := c.GlobalString("rpc-auth")
	cmd := "deploy"
	payload := []byte("fuck")

	rpcclient, err := command.RPCClient(rpcAddress, rpcAuthKey)
	if err != nil {
		log.Fatalf("Unable to connect to master at %s: %s", rpcAddress, err)
	}

	log.Printf("Sending %s command with payload %q", cmd, payload)
	err = rpcclient.UserEvent(cmd, payload, false)
	if err != nil {
		log.Fatal(err)
	}
	log.Print("OK")

}

/*
func leaveCluster() {
	//TODO this defer isnt running when ctrl-c'd
			go func() {
		    sig := <-sigChan
		        log.Printf("Cleaning up registered callback after signal %s\n", sig)
		                              os.Exit(0)
				log.Print("fuck")
				if s != nil {
					log.Print("Leaving cluster")
					err := s.Leave()
					if err != nil {
						log.Print(err)
						return
					}
					log.Print("Shutting down")
					err = s.Shutdown()
					if err != nil {
						log.Print(err)
						return
					}

				}
			}()
}
*/
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"net/http"
	"./config"
)

var cfg config.Configuration

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "happy new year from %s!", cfg.HostCountry)
}

func main() {
	var err error
	cfg, err = config.GetDefaultConfig()

	if err != nil {
		fmt.Println("Error while loading configuration file")
		os.Exit(1)
	}

	http.HandleFunc("/", hello)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Switch to gorilla/mux for routing<commit_after>package main

import (
	"fmt"
	"os"
	"net/http"
	"./config"
	"github.com/gorilla/mux"
)

var cfg config.Configuration

func HelloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "happy new year from %s!", cfg.HostCountry)
}

func main() {
	var err error
	cfg, err = config.GetDefaultConfig()

	if err != nil {
		fmt.Println("Error while loading configuration file")
		os.Exit(1)
	}

	router := mux.NewRouter()
	router.HandleFunc("/", HelloHandler)
	http.ListenAndServe(":8080", router)
}
<|endoftext|>


<commit_before>// Copyright 2014 Jongmin Kim. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main

import (
	"flag"
	"fmt"
	"go/scanner"
	"os"

	"github.com/atomaths/fng/term"
)

var (
	exitCode = 0
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: fng [file name patterns...] [match string]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

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

	for i := 0; i < flag.NArg(); i++ {
		fmt.Println(flag.Arg(i))
	}

	fmt.Println(term.Width())
}

func main() {
	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}
<commit_msg>add recursive flag<commit_after>// Copyright 2014 Jongmin Kim. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main

import (
	"flag"
	"fmt"
	"go/scanner"
	"os"

	"github.com/atomaths/fng/term"
)

var (
	exitCode = 0
	recursive bool
)

func init() {
	flag.BoolVar(&recursive, "r", false, "recursive")
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: fng [file name patterns...] [match string]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

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

	for i := 0; i < flag.NArg(); i++ {
		fmt.Println(flag.Arg(i))
	}

	fmt.Println(term.Width())
}

func main() {
	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"

	arg "github.com/alexflint/go-arg"
	"github.com/hako/durafmt"
	"github.com/joho/godotenv"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/common/model"
	"github.com/tucnak/telebot"
)

const (
	commandStart = "/start"
	commandStop  = "/stop"
	commandHelp  = "/help"
	commandUsers = "/users"

	commandStatus     = "/status"
	commandAlerts     = "/alerts"
	commandSilences   = "/silences"
	commandSilenceAdd = "/silence_add"
	commandSilence    = "/silence"
	commandSilenceDel = "/silence_del"

	responseStart = "Hey, %s! I will now keep you up to date!\n" + commandHelp
	responseStop  = "Alright, %s! I won't talk to you again.\n" + commandHelp
	responseHelp  = `
I'm a Prometheus AlertManager bot for Telegram. I will notify you about alerts.
You can also ask me about my ` + commandStatus + `, ` + commandAlerts + ` & ` + commandSilences + `

Available commands:
` + commandStart + ` - Subscribe for alerts.
` + commandStop + ` - Unsubscribe for alerts.
` + commandStatus + ` - Print the current status.
` + commandAlerts + ` - List all alerts.
` + commandSilences + ` - List all silences.
`
)

// Config knows all configurations from ENV
type Config struct {
	AlertmanagerURL string `arg:"env:ALERTMANAGER_URL"`
	TelegramToken   string `arg:"env:TELEGRAM_TOKEN"`
	TelegramAdmin   int    `arg:"env:TELEGRAM_ADMIN"`
	Store           string `arg:"env:STORE"`
}

func main() {
	log.Println("starting...")

	if err := godotenv.Load(); err != nil {
		log.Println(err)
	}

	var c Config
	arg.MustParse(&c)

	bot, err := telebot.NewBot(c.TelegramToken)
	if err != nil {
		log.Fatalln(err)
	}

	users, err := NewUserStore(c.Store)
	if err != nil {
		log.Fatalln(err)
	}

	messages := make(chan telebot.Message, 100)
	bot.Listen(messages, 1*time.Second)

	go HTTPListenAndServe(bot, users)

	for message := range messages {
		if message.Sender.ID != c.TelegramAdmin {
			log.Printf("dropped message from unallowed sender: %s(%d)", message.Sender.Username, message.Sender.ID)
			continue
		}

		switch message.Text {
		case commandStart:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStart, message.Sender.FirstName), nil)
			users.Add(message.Sender)
			log.Printf("User %s(%d) subscribed", message.Sender.Username, message.Sender.ID)
		case commandStop:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStop, message.Sender.FirstName), nil)
			users.Remove(message.Sender)
			log.Printf("User %s(%d) unsubscribed", message.Sender.Username, message.Sender.ID)
		case commandHelp:
			bot.SendMessage(message.Chat, responseHelp, nil)
		case commandUsers:
			bot.SendMessage(message.Chat, fmt.Sprintf("Currently %d users are subscribed.", users.Len()), nil)
		case commandStatus:
			s, err := status(c)
			if err != nil {
				bot.SendMessage(message.Chat, fmt.Sprintf("failed to get status... %v", err), nil)
			}

			uptime := durafmt.Parse(time.Since(s.Data.Uptime))

			bot.SendMessage(
				message.Chat,
				fmt.Sprintf("Version: %s\nUptime: %s", s.Data.VersionInfo.Version, uptime),
				nil,
			)
		case commandAlerts:
			alerts, err := listAlerts(c)
			if err != nil {
				bot.SendMessage(message.Chat, fmt.Sprintf("failed to list alerts... %v", err), nil)
			}

			if len(alerts) == 0 {
				bot.SendMessage(message.Chat, "No alerts right now! 🎉", nil)
			}

			var out string
			for _, a := range alerts {
				out = out + Message(a) + "\n"
			}

			bot.SendMessage(message.Chat, out, &telebot.SendOptions{ParseMode: telebot.ModeMarkdown})
		}
	}
}

// HTTPListenAndServe starts a http server and listens for incoming alerts to send to the users
func HTTPListenAndServe(bot *telebot.Bot, users *UserStore) {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var webhook notify.WebhookMessage

		var buf bytes.Buffer
		tee := io.TeeReader(r.Body, &buf)
		defer r.Body.Close()

		decoder := json.NewDecoder(tee)
		if err := decoder.Decode(&webhook); err != nil {
			log.Printf("failed to decode webhook message: %v\n", err)
		}

		body, err := ioutil.ReadAll(&buf)
		if err != nil {
			log.Printf("failed to read from request.Body for logging: %v", err)
		}
		log.Println(string(body))

		for _, alert := range webhook.Alerts {
			var out string
			out = out + Message(alert) + "\n"

			for _, user := range users.List() {
				bot.SendMessage(user, out, &telebot.SendOptions{ParseMode: telebot.ModeMarkdown})
			}
		}

		w.WriteHeader(http.StatusOK)
	})

	log.Fatalln(http.ListenAndServe(":8080", nil))
}

// Message converts an alert to a message string
func Message(a template.Alert) string {
	if a.Status == "" {
		if a.EndsAt.IsZero() {
			a.Status = string(model.AlertFiring)
		} else {
			a.Status = string(model.AlertResolved)
		}
	}

	status := a.Status
	switch status {
	case string(model.AlertFiring):
		status = "🔥 *" + strings.ToUpper(status) + "* 🔥"
	case string(model.AlertResolved):
		status = "*" + strings.ToUpper(status) + "*"
	}

	return fmt.Sprintf(
		"%s\n*%s* (%s)\n%s\n",
		status,
		a.Labels["alertname"],
		a.Annotations["summary"],
		a.Annotations["description"],
	)
}

type alertResponse struct {
	Status string           `json:"status"`
	Alerts []template.Alert `json:"data,omitempty"`
}

func listAlerts(c Config) ([]template.Alert, error) {
	resp, err := http.Get(c.AlertmanagerURL + "/api/v1/alerts")
	if err != nil {
		return nil, err
	}

	var alertResponse alertResponse
	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	dec.Decode(&alertResponse)

	return alertResponse.Alerts, err
}

type statusResponse struct {
	Status string `json:"status"`
	Data   struct {
		Uptime      time.Time `json:"uptime"`
		VersionInfo struct {
			Branch    string `json:"branch"`
			BuildDate string `json:"buildDate"`
			BuildUser string `json:"buildUser"`
			GoVersion string `json:"goVersion"`
			Revision  string `json:"revision"`
			Version   string `json:"version"`
		} `json:"versionInfo"`
	} `json:"data"`
}

func status(c Config) (statusResponse, error) {
	var statusResponse statusResponse

	resp, err := http.Get(c.AlertmanagerURL + "/api/v1/status")
	if err != nil {
		return statusResponse, err
	}

	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	dec.Decode(&statusResponse)

	return statusResponse, nil
}
<commit_msg>Print the start, end & duration of alerts in alert messages<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"

	arg "github.com/alexflint/go-arg"
	"github.com/hako/durafmt"
	"github.com/joho/godotenv"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/common/model"
	"github.com/tucnak/telebot"
)

const (
	commandStart = "/start"
	commandStop  = "/stop"
	commandHelp  = "/help"
	commandUsers = "/users"

	commandStatus     = "/status"
	commandAlerts     = "/alerts"
	commandSilences   = "/silences"
	commandSilenceAdd = "/silence_add"
	commandSilence    = "/silence"
	commandSilenceDel = "/silence_del"

	responseStart = "Hey, %s! I will now keep you up to date!\n" + commandHelp
	responseStop  = "Alright, %s! I won't talk to you again.\n" + commandHelp
	responseHelp  = `
I'm a Prometheus AlertManager bot for Telegram. I will notify you about alerts.
You can also ask me about my ` + commandStatus + `, ` + commandAlerts + ` & ` + commandSilences + `

Available commands:
` + commandStart + ` - Subscribe for alerts.
` + commandStop + ` - Unsubscribe for alerts.
` + commandStatus + ` - Print the current status.
` + commandAlerts + ` - List all alerts.
` + commandSilences + ` - List all silences.
`
)

// Config knows all configurations from ENV
type Config struct {
	AlertmanagerURL string `arg:"env:ALERTMANAGER_URL"`
	TelegramToken   string `arg:"env:TELEGRAM_TOKEN"`
	TelegramAdmin   int    `arg:"env:TELEGRAM_ADMIN"`
	Store           string `arg:"env:STORE"`
}

func main() {
	log.Println("starting...")

	if err := godotenv.Load(); err != nil {
		log.Println(err)
	}

	var c Config
	arg.MustParse(&c)

	bot, err := telebot.NewBot(c.TelegramToken)
	if err != nil {
		log.Fatalln(err)
	}

	users, err := NewUserStore(c.Store)
	if err != nil {
		log.Fatalln(err)
	}

	messages := make(chan telebot.Message, 100)
	bot.Listen(messages, 1*time.Second)

	go HTTPListenAndServe(bot, users)

	for message := range messages {
		if message.Sender.ID != c.TelegramAdmin {
			log.Printf("dropped message from unallowed sender: %s(%d)", message.Sender.Username, message.Sender.ID)
			continue
		}

		switch message.Text {
		case commandStart:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStart, message.Sender.FirstName), nil)
			users.Add(message.Sender)
			log.Printf("User %s(%d) subscribed", message.Sender.Username, message.Sender.ID)
		case commandStop:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStop, message.Sender.FirstName), nil)
			users.Remove(message.Sender)
			log.Printf("User %s(%d) unsubscribed", message.Sender.Username, message.Sender.ID)
		case commandHelp:
			bot.SendMessage(message.Chat, responseHelp, nil)
		case commandUsers:
			bot.SendMessage(message.Chat, fmt.Sprintf("Currently %d users are subscribed.", users.Len()), nil)
		case commandStatus:
			s, err := status(c)
			if err != nil {
				bot.SendMessage(message.Chat, fmt.Sprintf("failed to get status... %v", err), nil)
			}

			uptime := durafmt.Parse(time.Since(s.Data.Uptime))

			bot.SendMessage(
				message.Chat,
				fmt.Sprintf("Version: %s\nUptime: %s", s.Data.VersionInfo.Version, uptime),
				nil,
			)
		case commandAlerts:
			alerts, err := listAlerts(c)
			if err != nil {
				bot.SendMessage(message.Chat, fmt.Sprintf("failed to list alerts... %v", err), nil)
			}

			if len(alerts) == 0 {
				bot.SendMessage(message.Chat, "No alerts right now! 🎉", nil)
			}

			var out string
			for _, a := range alerts {
				out = out + Message(a) + "\n"
			}

			bot.SendMessage(message.Chat, out, &telebot.SendOptions{ParseMode: telebot.ModeMarkdown})
		}
	}
}

// HTTPListenAndServe starts a http server and listens for incoming alerts to send to the users
func HTTPListenAndServe(bot *telebot.Bot, users *UserStore) {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var webhook notify.WebhookMessage

		var buf bytes.Buffer
		tee := io.TeeReader(r.Body, &buf)
		defer r.Body.Close()

		decoder := json.NewDecoder(tee)
		if err := decoder.Decode(&webhook); err != nil {
			log.Printf("failed to decode webhook message: %v\n", err)
		}

		body, err := ioutil.ReadAll(&buf)
		if err != nil {
			log.Printf("failed to read from request.Body for logging: %v", err)
		}
		log.Println(string(body))

		for _, alert := range webhook.Alerts {
			var out string
			out = out + Message(alert) + "\n"

			for _, user := range users.List() {
				bot.SendMessage(user, out, &telebot.SendOptions{ParseMode: telebot.ModeMarkdown})
			}
		}

		w.WriteHeader(http.StatusOK)
	})

	log.Fatalln(http.ListenAndServe(":8080", nil))
}

// Message converts an alert to a message string
func Message(a template.Alert) string {
	if a.Status == "" {
		if a.EndsAt.IsZero() {
			a.Status = string(model.AlertFiring)
		} else {
			a.Status = string(model.AlertResolved)
		}
	}

	var duration string

	status := a.Status
	switch status {
	case string(model.AlertFiring):
		status = "🔥 *" + strings.ToUpper(status) + "* 🔥"
		duration = fmt.Sprintf("*Started*: %s ago", durafmt.Parse(time.Since(a.StartsAt)))
	case string(model.AlertResolved):
		status = "*" + strings.ToUpper(status) + "*"
		duration = fmt.Sprintf(
			"*Ended*: %s ago\n*Duration*: %s",
			durafmt.Parse(time.Since(a.EndsAt)),
			durafmt.Parse(a.EndsAt.Sub(a.StartsAt)),
		)
	}

	return fmt.Sprintf(
		"%s\n*%s* (%s)\n%s\n%s\n",
		status,
		a.Labels["alertname"],
		a.Annotations["summary"],
		a.Annotations["description"],
		duration,
	)
}

type alertResponse struct {
	Status string           `json:"status"`
	Alerts []template.Alert `json:"data,omitempty"`
}

func listAlerts(c Config) ([]template.Alert, error) {
	resp, err := http.Get(c.AlertmanagerURL + "/api/v1/alerts")
	if err != nil {
		return nil, err
	}

	var alertResponse alertResponse
	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	dec.Decode(&alertResponse)

	return alertResponse.Alerts, err
}

type statusResponse struct {
	Status string `json:"status"`
	Data   struct {
		Uptime      time.Time `json:"uptime"`
		VersionInfo struct {
			Branch    string `json:"branch"`
			BuildDate string `json:"buildDate"`
			BuildUser string `json:"buildUser"`
			GoVersion string `json:"goVersion"`
			Revision  string `json:"revision"`
			Version   string `json:"version"`
		} `json:"versionInfo"`
	} `json:"data"`
}

func status(c Config) (statusResponse, error) {
	var statusResponse statusResponse

	resp, err := http.Get(c.AlertmanagerURL + "/api/v1/status")
	if err != nil {
		return statusResponse, err
	}

	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	dec.Decode(&statusResponse)

	return statusResponse, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"gopkg.in/alecthomas/kingpin.v1"
	"os"
	"strconv"
	"time"
)

const (
	ENV_ENANOS_PORT string = "ENANOS_PORT"
)

var (
	verbose     = kingpin.Flag("verbose", "Enable verbose mode.").Bool()
	port        = kingpin.Flag("port", "the port to host the server on").Default("8000").Short('p').OverrideDefaultFromEnvar(ENV_ENANOS_PORT).Int()
	minSleep    = kingpin.Flag("min-sleep", "the minimum sleep time for the wait endpoint e.g. 5ms, 5s, 5m etc...").Default("1s").String()
	maxSleep    = kingpin.Flag("max-sleep", "the maximum sleep time for the wait endpoint e.g. 5ms, 5s, 5m etc...").Default("60s").String()
	randomSleep = kingpin.Flag("random-sleep", "whether to sleep a random time between min and max or just the max").Default("true").Bool()
	minSize     = kingpin.Flag("min-size", "the minimum size of response body for sneezy to generate").Default("1024").Int()
	maxSize     = kingpin.Flag("max-size", "the maximum size of response body for sneezy to generate").Default(strconv.Itoa(1024 * 100)).Int()
	randomSize  = kingpin.Flag("random-size", "whether to return a random sized payload between min and max or just max").Default("true").Bool()
	content     = kingpin.Flag("content", "the content to return for OK responses").Default("hello world").String()
	contentType = kingpin.Flag("content-type", "the content type to return for OK responses").Default("text/plain").String()
)

func responseCodeGeneratorFactory(codes []int) ResponseCodeGenerator {
	return NewRandomResponseCodeGenerator(codes)
}

func main() {
	kingpin.Version("0.0.1")
	kingpin.CommandLine.Help = `Enanos is an investigation tool in the form of a HTTP server with several endpoints that can be used to substitute the actual http service dependencies of a system.  This tool allows you to see how a system will perform against varying un-stable http services, each which exhibit different effects.

	/success		- will return a 200 response code
	/server_error		- will return a random 5XX response code
	/content_size		- will return a 200 response code but a response body with a size between <minSize> and <maxSize>.  The content returned will be random or a mangled version of the content which has been configured to return i.e. it cannot guarantee to meet any content-types configured in that it will be malformed.
	/wait			- will return a 200 response code but only after a random sleep between <minSleep> and <maxSleep>
	/redirect		- will return a random 3XX response code.  If the response code is one which redirects then Bashful will return its own location to invite an infinite redirect loop
	/client_error		- will return a random 4XX response code
	/dead_or_alive	- will kill the server and only bring it back online after configured amount of time (ms) has passed

	/defined?code=<code>	- will return the specified http status code
	`
	kingpin.Parse()
	var snoozer Snoozer
	var responseBodyGenerator ResponseBodyGenerator

	if *randomSize {
		responseBodyGenerator = NewRandomResponseBodyGenerator(*minSize, *maxSize)
	} else {
		responseBodyGenerator = NewMaxResponseBodyGenerator(*maxSize)
	}

	minSleepValue, minSleepErr := time.ParseDuration(*minSleep)
	maxSleepValue, maxSleepErr := time.ParseDuration(*maxSleep)

	if minSleepErr != nil || maxSleepErr != nil {
		fmt.Errorf("Invalid duration specified for min or max sleep")
		os.Exit(1)
	}

	if *randomSleep {
		snoozer = NewRandomSnoozer(minSleepValue, maxSleepValue)
	} else {
		snoozer = NewMaxSnoozer(maxSleepValue)
	}

	config := Config{*port, *verbose, *content, *contentType}
	fmt.Println(fmt.Sprintf("Enanos Server listening on port %d", *port))
	StartEnanos(config, responseBodyGenerator, responseCodeGeneratorFactory, snoozer)
}
<commit_msg>Using the humanize package to allow for easier input of the desired size of response for the content_size endpoint. This closes #2<commit_after>package main

import (
	"fmt"
	"github.com/dustin/go-humanize"
	"gopkg.in/alecthomas/kingpin.v1"
	"os"
	"time"
)

const (
	ENV_ENANOS_PORT string = "ENANOS_PORT"
)

var (
	verbose     = kingpin.Flag("verbose", "Enable verbose mode.").Bool()
	port        = kingpin.Flag("port", "the port to host the server on").Default("8000").Short('p').OverrideDefaultFromEnvar(ENV_ENANOS_PORT).Int()
	minSleep    = kingpin.Flag("min-sleep", "the minimum sleep time for the wait endpoint e.g. 5ms, 5s, 5m etc...").Default("1s").String()
	maxSleep    = kingpin.Flag("max-sleep", "the maximum sleep time for the wait endpoint e.g. 5ms, 5s, 5m etc...").Default("60s").String()
	randomSleep = kingpin.Flag("random-sleep", "whether to sleep a random time between min and max or just the max").Default("true").Bool()
	minSize     = kingpin.Flag("min-size", "the minimum size of response body for the content_size endpoint e.g. 5B, 5KB, 5MB etc...").Default("10KB").String()
	maxSize     = kingpin.Flag("max-size", "the maximum size of response body for the content_size endpoint e.g. 5B, 5KB, 5MB etc...").Default("100KB").String()
	randomSize  = kingpin.Flag("random-size", "whether to return a random sized payload between min and max or just max").Default("true").Bool()
	content     = kingpin.Flag("content", "the content to return for OK responses").Default("hello world").String()
	contentType = kingpin.Flag("content-type", "the content type to return for OK responses").Default("text/plain").String()
)

func responseCodeGeneratorFactory(codes []int) ResponseCodeGenerator {
	return NewRandomResponseCodeGenerator(codes)
}

func main() {
	kingpin.Version("0.0.1")
	kingpin.CommandLine.Help = `Enanos is an investigation tool in the form of a HTTP server with several endpoints that can be used to substitute the actual http service dependencies of a system.  This tool allows you to see how a system will perform against varying un-stable http services, each which exhibit different effects.

	/success		- will return a 200 response code
	/server_error		- will return a random 5XX response code
	/content_size		- will return a 200 response code but a response body with a size between <minSize> and <maxSize>.  The content returned will be random or a mangled version of the content which has been configured to return i.e. it cannot guarantee to meet any content-types configured in that it will be malformed.
	/wait			- will return a 200 response code but only after a random sleep between <minSleep> and <maxSleep>
	/redirect		- will return a random 3XX response code.  If the response code is one which redirects then Bashful will return its own location to invite an infinite redirect loop
	/client_error		- will return a random 4XX response code
	/dead_or_alive	- will kill the server and only bring it back online after configured amount of time (ms) has passed

	/defined?code=<code>	- will return the specified http status code
	`
	kingpin.Parse()
	var snoozer Snoozer
	var responseBodyGenerator ResponseBodyGenerator

	minSizeValue, minSizeErr := humanize.ParseBytes(*minSize)
	maxSizeValue, maxSizeErr := humanize.ParseBytes(*maxSize)

	if minSizeErr != nil || maxSizeErr != nil {
		fmt.Errorf("Invalid size specified for min or max size")
		os.Exit(1)
	}

	if *randomSize {
		responseBodyGenerator = NewRandomResponseBodyGenerator(int(minSizeValue), int(maxSizeValue))
	} else {
		responseBodyGenerator = NewMaxResponseBodyGenerator(int(maxSizeValue))
	}

	minSleepValue, minSleepErr := time.ParseDuration(*minSleep)
	maxSleepValue, maxSleepErr := time.ParseDuration(*maxSleep)

	if minSleepErr != nil || maxSleepErr != nil {
		fmt.Errorf("Invalid duration specified for min or max sleep")
		os.Exit(1)
	}

	if *randomSleep {
		snoozer = NewRandomSnoozer(minSleepValue, maxSleepValue)
	} else {
		snoozer = NewMaxSnoozer(maxSleepValue)
	}

	config := Config{*port, *verbose, *content, *contentType}
	fmt.Println(fmt.Sprintf("Enanos Server listening on port %d", *port))
	StartEnanos(config, responseBodyGenerator, responseCodeGeneratorFactory, snoozer)
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "github.com/dotcloud/docker/archive"
    "io"
    "io/ioutil"
    "net/http"
    "os"
    "crypto/tls"
    "crypto/x509"
)

func usage() {
    fmt.Printf("Usage: %s https://example.org/download.tar.bz2\n", os.Args[0])
    os.Exit(1)
}

func tls_config() *tls.Config {
    pool := x509.NewCertPool()
    cert, err := ioutil.ReadFile("./.cert")
    if err != nil {
        fmt.Printf("Failed to load certificate -- %s\n", err)
        os.Exit(1)
    }
    pool.AppendCertsFromPEM(cert)
    return &tls.Config{RootCAs: pool}
}

func download(url string) io.Reader {
    transport := &http.Transport{TLSClientConfig: tls_config()}
    client := &http.Client{Transport: transport}
    response, err := client.Get(url)
    if err != nil {
        fmt.Printf("Failed to download %s -- %s\n", url, err)
        os.Exit(1)
    }
    fmt.Printf("Downloaded %s\n", url)
    return response.Body
}

func main() {
    if len(os.Args) < 2 {
        usage()
    }
    err := archive.Untar(download(os.Args[1]), "/", nil)
    if err != nil {
        fmt.Printf("Failed to extract -- %s\n", err)
    }
    fmt.Println("Successfully extracted archive")
}
<commit_msg>gofmt hates space<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"github.com/dotcloud/docker/archive"
	"io"
	"io/ioutil"
	"net/http"
	"os"
)

func usage() {
	fmt.Printf("Usage: %s https://example.org/download.tar.bz2\n", os.Args[0])
	os.Exit(1)
}

func tls_config() *tls.Config {
	pool := x509.NewCertPool()
	cert, err := ioutil.ReadFile("./.cert")
	if err != nil {
		fmt.Printf("Failed to load certificate -- %s\n", err)
		os.Exit(1)
	}
	pool.AppendCertsFromPEM(cert)
	return &tls.Config{RootCAs: pool}
}

func download(url string) io.Reader {
	transport := &http.Transport{TLSClientConfig: tls_config()}
	client := &http.Client{Transport: transport}
	response, err := client.Get(url)
	if err != nil {
		fmt.Printf("Failed to download %s -- %s\n", url, err)
		os.Exit(1)
	}
	fmt.Printf("Downloaded %s\n", url)
	return response.Body
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}
	err := archive.Untar(download(os.Args[1]), "/", nil)
	if err != nil {
		fmt.Printf("Failed to extract -- %s\n", err)
	}
	fmt.Println("Successfully extracted archive")
}
<|endoftext|>


<commit_before>package main

/*
havealook:gerkin goconvey

Todo:unit tests and benchmarks
Todo:error handling
Todo:graceful shutdown via signals
Todo: error messages
Todo: fileviewer
*/
import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strconv"
)

//all types of resources we can be dealing with
const (
	AppName                    string = "DirWalker"
	DefaultTemplateString      string = "{{.Mode}}	{{.Name}}	{{.Size}}	{{.ModTime}}	"
	DefaultTemplateName        string = "x" //unique name, if user want he can override default template with x template name
	FileTemplateName           string = "-"
	ModeDirTemplateName        string = "d"
	ModeExclusiveTemplateName  string = "l"
	ModeTemporaryTemplateName  string = "T"
	ModeSymlinkTemplateName    string = "L"
	ModeDeviceTemplateName     string = "D"
	ModeNamedPipeTemplateName  string = "p"
	ModeSocketTemplateName     string = "S"
	ModeSetuidTemplateName     string = "u"
	ModeSetgidTemplateName     string = "g"
	ModeCharDeviceTemplateName string = "c"
	ModeStickyTemplateName     string = "t"

	StartingFolder string = "/Users/chrisrozacki/Desktop/music/brian  eno/brian eno - 1973 here come the warm jets"

	ModTimelayout = "Jan 2, 2006 at 3:04pm"

	//error code
	//log file path
	LogFileName string = "log.txt"
)

//error codes
const ()

//error type
type Error struct {
	Op   string
	Err  error
	Port int
	Path string
}

//error type implements Error() method
func (err Error) Error() string {
	return fmt.Sprintf("error: %s while listen and server for port %d", err.Err.Error(), err.Port)
}

//walker mainly keeps
type DirWalker struct {
	//
	DebugMode bool
	//
	WriterA Writer
}

func CreateDirWalker(debug bool, format string) *DirWalker {
	walker := DirWalker{}
	log.Printf("created walker: %p\n", &walker)

	switch format {
	case "html":
		walker.WriterA = &TemplateWriter{}
		walker.WriterA.Init()
	case "json":
		walker.WriterA = &JSONWriter{}

	}
	log.Printf("created walker: %p\n", &walker)
	return &walker
}

func (self DirWalker) Start(urlPath string, nic string, port int) error {
	http.Handle(urlPath, self)

	err := http.ListenAndServe(":"+strconv.Itoa(port), nil)

	if err != nil {
		return Error{Op: "listen and serve", Err: err, Port: port}
	}
	return nil
}

func (self DirWalker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var ok bool
	dir := "/"
	dirs := r.URL.Query()["dir"]
	if dirs != nil {
		dir = filepath.Clean(dirs[0])
		//log.Printf("dir=%s\n", dir)
	}
	items, err := ioutil.ReadDir(dir)
	if err != nil {
		if items, err = ioutil.ReadDir("/"); err != nil {
			panic(err)
		}
	}

	sortBy 		:= "name"
	if len(r.URL.Query()["sort"])>0{
		sortBy=r.URL.Query()["sort"][0]
	}

	sortDir	:=	false
	if _,ok	= r.URL.Query()["dir"];ok{
		sortDir=true
	}

	var sortableFileInfo SortableFileInfo = SortableFileInfo{Data: items, SortBy: sortBy, Dir: sortDir}
	sort.Sort(sortableFileInfo)

	dirs = nil
	SplitPath(dir, &dirs)

	infoMap := map[string]interface{}{
		"Path":       dir,
		"Breadcrumb": dirs,
	}

	self.WriterA.WriteHeader(w, infoMap, 0, "ok")
	for _, info := range items {

		//convert struct to map and send it to the template
		infoMap := map[string]interface{}{
			"Name":    info.Name(),
			"Size":    info.Size(),
			"IsDir":   info.IsDir(),
			"Mode":    info.Mode(),
			"ModTime": info.ModTime(),
			"Path":    filepath.Join(dir, info.Name()),
		}

		self.WriterA.WriteItem(w, infoMap)
	}
	self.WriterA.WriteFooter(w, nil)
}

func SplitPath(path string, dirs *[]string) {
	*dirs = append(*dirs, path)
	//log.Println(path, " ", len(path))
	//if nothing left then we stop
	if len(path) <= 1 {
		if len(path) == 0 {
			*dirs = append(*dirs, "/")
		}
		return
	}
	element := filepath.Base(path)
	fmt.Println(element)
	//starting index:ending indexes
	path = path[0 : (len(path)-len(element))-1]
	SplitPath(path, dirs)
}

func SetLogFile(logPath string) error {

	f, err := os.Create(logPath)
	if err != nil {
		return Error{Op: "file open", Err: err, Path: logPath}
	}
	defer f.Close()

	log.SetOutput(f)
	return nil
}

func main() {

	//set log file
	//SetLogFile(LogFileName)

	log.Println("staring ", AppName)

	//wait for signals
	//.....

	//parse command line parameters
	nic := flag.String("nic", "localhost", "")

	port := flag.Int("port", 8080, "")
	debug := flag.Bool("debug", false, "")
	urlPath := flag.String("path", "/", "")
	format := flag.String("format", "json", "")
	flag.Parse()

	//initialize the mani structure
	dw := CreateDirWalker(*debug, *format)
	log.Printf("returne walker: %p ", dw)
	//star the server
	err := dw.Start(*urlPath, *nic, *port)
	if err != nil {
		log.Println(err)
		log.Println("server will stop")
		return
	}

	log.Println("server started")
}
<commit_msg>#7 issue fixed, use sd= to change sorting direction<commit_after>package main

/*
havealook:gerkin goconvey

Todo:unit tests and benchmarks
Todo:error handling
Todo:graceful shutdown via signals
Todo: error messages
Todo: fileviewer
*/
import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strconv"
)

//all types of resources we can be dealing with
const (
	AppName                    string = "DirWalker"
	DefaultTemplateString      string = "{{.Mode}}	{{.Name}}	{{.Size}}	{{.ModTime}}	"
	DefaultTemplateName        string = "x" //unique name, if user want he can override default template with x template name
	FileTemplateName           string = "-"
	ModeDirTemplateName        string = "d"
	ModeExclusiveTemplateName  string = "l"
	ModeTemporaryTemplateName  string = "T"
	ModeSymlinkTemplateName    string = "L"
	ModeDeviceTemplateName     string = "D"
	ModeNamedPipeTemplateName  string = "p"
	ModeSocketTemplateName     string = "S"
	ModeSetuidTemplateName     string = "u"
	ModeSetgidTemplateName     string = "g"
	ModeCharDeviceTemplateName string = "c"
	ModeStickyTemplateName     string = "t"

	StartingFolder string = "/Users/chrisrozacki/Desktop/music/brian  eno/brian eno - 1973 here come the warm jets"

	ModTimelayout = "Jan 2, 2006 at 3:04pm"

	//error code
	//log file path
	LogFileName string = "log.txt"
)

//error codes
const ()

//error type
type Error struct {
	Op   string
	Err  error
	Port int
	Path string
}

//error type implements Error() method
func (err Error) Error() string {
	return fmt.Sprintf("error: %s while listen and server for port %d", err.Err.Error(), err.Port)
}

//walker mainly keeps
type DirWalker struct {
	//
	DebugMode bool
	//
	WriterA Writer
}

func CreateDirWalker(debug bool, format string) *DirWalker {
	walker := DirWalker{}
	log.Printf("created walker: %p\n", &walker)

	switch format {
	case "html":
		walker.WriterA = &TemplateWriter{}
		walker.WriterA.Init()
	case "json":
		walker.WriterA = &JSONWriter{}

	}
	log.Printf("created walker: %p\n", &walker)
	return &walker
}

func (self DirWalker) Start(urlPath string, nic string, port int) error {
	http.Handle(urlPath, self)

	err := http.ListenAndServe(":"+strconv.Itoa(port), nil)

	if err != nil {
		return Error{Op: "listen and serve", Err: err, Port: port}
	}
	return nil
}

func (self DirWalker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var ok bool
	dir := "/"
	dirs := r.URL.Query()["dir"]
	if dirs != nil {
		dir = filepath.Clean(dirs[0])
		//log.Printf("dir=%s\n", dir)
	}
	items, err := ioutil.ReadDir(dir)
	if err != nil {
		if items, err = ioutil.ReadDir("/"); err != nil {
			panic(err)
		}
	}

	sortBy 		:= "name"
	if len(r.URL.Query()["sort"])>0{
		sortBy=r.URL.Query()["sort"][0]
	}

	sortDir	:=	false
	if _,ok	= r.URL.Query()["sd"];ok{
		sortDir=true
	}

	var sortableFileInfo SortableFileInfo = SortableFileInfo{Data: items, SortBy: sortBy, Dir: sortDir}
	sort.Sort(sortableFileInfo)

	dirs = nil
	SplitPath(dir, &dirs)

	infoMap := map[string]interface{}{
		"Path":       dir,
		"Breadcrumb": dirs,
	}

	self.WriterA.WriteHeader(w, infoMap, 0, "ok")
	for _, info := range items {

		//convert struct to map and send it to the template
		infoMap := map[string]interface{}{
			"Name":    info.Name(),
			"Size":    info.Size(),
			"IsDir":   info.IsDir(),
			"Mode":    info.Mode(),
			"ModTime": info.ModTime(),
			"Path":    filepath.Join(dir, info.Name()),
		}

		self.WriterA.WriteItem(w, infoMap)
	}
	self.WriterA.WriteFooter(w, nil)
}

func SplitPath(path string, dirs *[]string) {
	*dirs = append(*dirs, path)
	//log.Println(path, " ", len(path))
	//if nothing left then we stop
	if len(path) <= 1 {
		if len(path) == 0 {
			*dirs = append(*dirs, "/")
		}
		return
	}
	element := filepath.Base(path)
	fmt.Println(element)
	//starting index:ending indexes
	path = path[0 : (len(path)-len(element))-1]
	SplitPath(path, dirs)
}

func SetLogFile(logPath string) error {

	f, err := os.Create(logPath)
	if err != nil {
		return Error{Op: "file open", Err: err, Path: logPath}
	}
	defer f.Close()

	log.SetOutput(f)
	return nil
}

func main() {

	//set log file
	//SetLogFile(LogFileName)

	log.Println("staring ", AppName)

	//wait for signals
	//.....

	//parse command line parameters
	nic := flag.String("nic", "localhost", "")

	port := flag.Int("port", 8080, "")
	debug := flag.Bool("debug", false, "")
	urlPath := flag.String("path", "/", "")
	format := flag.String("format", "json", "")
	flag.Parse()

	//initialize the mani structure
	dw := CreateDirWalker(*debug, *format)
	log.Printf("returne walker: %p ", dw)
	//star the server
	err := dw.Start(*urlPath, *nic, *port)
	if err != nil {
		log.Println(err)
		log.Println("server will stop")
		return
	}

	log.Println("server started")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/Shopify/sarama"
	"golang.org/x/net/websocket"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"
	"path/filepath"
)

type ConsumerConfig struct {
	Broker    string `json:"broker"`
	Partition int    `json:"partition"`
	Topic     string `json:"topic"`
	Offset    string `json:"offset"`
}

type Config struct {
	Consumers []ConsumerConfig `json:"consumers"`
}

type Link struct {
	Url   string
	Title string
}

func startConsumers(config *Config, c chan *sarama.ConsumerMessage, quit chan bool) {
	for _, consumerConfig := range config.Consumers {
		topic, broker, partition := consumerConfig.Topic, consumerConfig.Broker, consumerConfig.Partition
		var offset int64 = -1

		if numericOffset, err := strconv.ParseInt(consumerConfig.Offset, 10, 64); err == nil {
			offset = numericOffset
		} else {
			switch consumerConfig.Offset {
			case "oldest":
				offset = -2
			case "newest":
				offset = -1
			default:
				log.Println("Invalid value for consumer offset")
				quit <- true
				return
			}
		}

		go consume(c, quit, topic, broker, partition, offset)
	}
}

func onConnected(ws *websocket.Conn) {
	log.Println("Opened WebSocket connection!")

	var config Config
	err := websocket.JSON.Receive(ws, &config)
	if err != nil {
		ws.Close()
		log.Println("Didn't receive config from WebSocket!", err)
		return
	}

	quit := make(chan bool)
	c := make(chan *sarama.ConsumerMessage)

	startConsumers(&config, c, quit)

	for {
		select {
		case consumerMessage := <-c:
			msg :=
				"{\"topic\": \"" + consumerMessage.Topic +
					"\", \"partition\": \"" + strconv.FormatInt(int64(consumerMessage.Partition), 10) +
					"\", \"offset\": \"" + strconv.FormatInt(consumerMessage.Offset, 10) +
					"\", \"key\": \"" + strings.Replace(string(consumerMessage.Key), `"`, `\"`, -1) +
					"\", \"value\": \"" + strings.Replace(string(consumerMessage.Value), `"`, `\"`, -1) +
					"\", \"consumedUnixTimestamp\": \"" + strconv.FormatInt(time.Now().Unix(), 10) +
					"\"}\n"

			log.Println("Sending message to WebSocket: " + msg)
			err := websocket.Message.Send(ws, msg)
			if err != nil {
				log.Println("Error while trying to send to WebSocket: ", err)
				quit <- true
			}
		case <-quit:
			err := ws.Close()
			log.Println("Closed WebSocket connection given quit signal.")
			if err != nil {
				log.Println("Error while closing WebSocket!: ", err)
			}
			return
		}
	}
}

func baseHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" && r.URL.RawQuery == "" {
		serveBaseHTML(w, r)
	} else {
		log.Println(r.URL.Path)
		chttp.ServeHTTP(w, r)
	}
}

func serveBaseHTML(w http.ResponseWriter, r *http.Request) {
	baseHTML := `
		<!DOCTYPE html>

		    <title>Flowbro</title>

		        {{range $i, $e := .}}

		            {{.Title}}

{{end}}

	`

	files, err := ioutil.ReadDir("webroot/configs")
	if err != nil {
		log.Fatal(err)
	}

	links := []Link{}
	for _, file := range files {
		config := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
		links = append(links, Link{
			Url:   "?config=" + config,
			Title: config,
		})
	}

	templ, err := template.New("base").Parse(baseHTML)
	if err != nil {
		log.Fatal(err)
	}

	err = templ.Execute(w, links)
	if err != nil {
		log.Fatal(err)
	}
}

func listenToSignals() {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		<-signals
		os.Exit(1)
	}()
}

var chttp = http.NewServeMux()

func main() {
	listenToSignals()

	http.Handle("/ws", websocket.Handler(onConnected))
	chttp.Handle("/", http.FileServer(http.Dir("webroot")))
	http.HandleFunc("/", baseHandler)

	if len(os.Args) == 1 {
		fmt.Println("usage: flowbro {portToServeOn}\n")
		os.Exit(2)
	}

	port := os.Args[1]
	err := http.ListenAndServe(":"+string(port), nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}
<commit_msg>Defaults to port 8080.<commit_after>package main

import (
	"fmt"
	"github.com/Shopify/sarama"
	"golang.org/x/net/websocket"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"
	"path/filepath"
)

type ConsumerConfig struct {
	Broker    string `json:"broker"`
	Partition int    `json:"partition"`
	Topic     string `json:"topic"`
	Offset    string `json:"offset"`
}

type Config struct {
	Consumers []ConsumerConfig `json:"consumers"`
}

type Link struct {
	Url   string
	Title string
}

func startConsumers(config *Config, c chan *sarama.ConsumerMessage, quit chan bool) {
	for _, consumerConfig := range config.Consumers {
		topic, broker, partition := consumerConfig.Topic, consumerConfig.Broker, consumerConfig.Partition
		var offset int64 = -1

		if numericOffset, err := strconv.ParseInt(consumerConfig.Offset, 10, 64); err == nil {
			offset = numericOffset
		} else {
			switch consumerConfig.Offset {
			case "oldest":
				offset = -2
			case "newest":
				offset = -1
			default:
				log.Println("Invalid value for consumer offset")
				quit <- true
				return
			}
		}

		go consume(c, quit, topic, broker, partition, offset)
	}
}

func onConnected(ws *websocket.Conn) {
	log.Println("Opened WebSocket connection!")

	var config Config
	err := websocket.JSON.Receive(ws, &config)
	if err != nil {
		ws.Close()
		log.Println("Didn't receive config from WebSocket!", err)
		return
	}

	quit := make(chan bool)
	c := make(chan *sarama.ConsumerMessage)

	startConsumers(&config, c, quit)

	for {
		select {
		case consumerMessage := <-c:
			msg :=
				"{\"topic\": \"" + consumerMessage.Topic +
					"\", \"partition\": \"" + strconv.FormatInt(int64(consumerMessage.Partition), 10) +
					"\", \"offset\": \"" + strconv.FormatInt(consumerMessage.Offset, 10) +
					"\", \"key\": \"" + strings.Replace(string(consumerMessage.Key), `"`, `\"`, -1) +
					"\", \"value\": \"" + strings.Replace(string(consumerMessage.Value), `"`, `\"`, -1) +
					"\", \"consumedUnixTimestamp\": \"" + strconv.FormatInt(time.Now().Unix(), 10) +
					"\"}\n"

			log.Println("Sending message to WebSocket: " + msg)
			err := websocket.Message.Send(ws, msg)
			if err != nil {
				log.Println("Error while trying to send to WebSocket: ", err)
				quit <- true
			}
		case <-quit:
			err := ws.Close()
			log.Println("Closed WebSocket connection given quit signal.")
			if err != nil {
				log.Println("Error while closing WebSocket!: ", err)
			}
			return
		}
	}
}

func baseHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" && r.URL.RawQuery == "" {
		serveBaseHTML(w, r)
	} else {
		log.Println(r.URL.Path)
		chttp.ServeHTTP(w, r)
	}
}

func serveBaseHTML(w http.ResponseWriter, r *http.Request) {
	baseHTML := `
		<!DOCTYPE html>

		    <title>Flowbro</title>

		        {{range $i, $e := .}}

		            {{.Title}}

{{end}}

	`

	files, err := ioutil.ReadDir("webroot/configs")
	if err != nil {
		log.Fatal(err)
	}

	links := []Link{}
	for _, file := range files {
		config := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
		links = append(links, Link{
			Url:   "?config=" + config,
			Title: config,
		})
	}

	templ, err := template.New("base").Parse(baseHTML)
	if err != nil {
		log.Fatal(err)
	}

	err = templ.Execute(w, links)
	if err != nil {
		log.Fatal(err)
	}
}

func listenToSignals() {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		<-signals
		os.Exit(1)
	}()
}

var chttp = http.NewServeMux()

func main() {
	listenToSignals()

	http.Handle("/ws", websocket.Handler(onConnected))
	chttp.Handle("/", http.FileServer(http.Dir("webroot")))
	http.HandleFunc("/", baseHandler)

	port := "8080"
	if len(os.Args) >= 2 {
		port = os.Args[1]
	}

	fmt.Printf("Flowbro is your bro on localhost:%v!\n", port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}
<|endoftext|>