<commit_before>package main

//go:generate go-bindata -o "bindata/bindata.go" -pkg "bindata" "data/..."
import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"

	"github.com/owm-inc/weatherchecker-go/bindata"
	"github.com/owm-inc/weatherchecker-go/db"
	"github.com/owm-inc/weatherchecker-go/structs"
)

type JsonResponse struct {
	Status  int         `json:"status"`
	Message string      `json:"message"`
	Content interface{} `json:"content"`
}

var sources = structs.CreateSources()

var mongoDsn string

var db_instance = db.Db()
var locations = structs.NewLocationTable(db_instance)
var history = structs.NewWeatherHistory(db_instance)

func MarshalPrintStuff(stuff interface{}, w http.ResponseWriter) {
	data, _ := json.Marshal(stuff)
	jsonString := string(data)
	fmt.Fprintf(w, jsonString)
}

func MarshalPrintResponse(status int, message string, content interface{}, w http.ResponseWriter) {
	MarshalPrintStuff(JsonResponse{Status: status, Message: message, Content: content}, w)
}

func PrintLocationEntry(locationEntry structs.LocationEntry, w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.LocationEntry{"location_entry": []structs.LocationEntry{locationEntry}}, w)
}

func PrintHistoryEntry(historyEntry []structs.HistoryDataEntry, w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.HistoryDataEntry{"history_entry": historyEntry}, w)
}

func PrintLocations(w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.LocationEntry{"locations": locations.ReadLocations()}, w)
}

func PrintSources(w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.SourceEntry{"sources": sources}, w)
}

func PrintStatus(err error, successMessage string, w http.ResponseWriter) {
	var status int
	var message string

	if err != nil {
		status = 500
		message = err.Error()
	} else {
		status = 200
		message = successMessage
	}

	MarshalPrintResponse(status, message, make(map[string]string), w)
}

func NotAllowedForPublic(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintStatus(errors.New("This entrypoint has been disabled for public installation of Weather Checker."), "", w)
}

func InvalidApiKey(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintStatus(errors.New("Invalid API key."), "", w)
}

func ValidApiKey(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintStatus(nil, "API key valid.", w)
}

func MakeMissingParamsList(query_holder url.Values, required_params []string) (missing []string) {
	for _, entry := range required_params {
		if query_holder.Get(entry) == "" {
			missing = append(missing, entry)
		}
	}

	return missing
}

func ReadSources(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintSources(w)
}

func CreateLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	missing := MakeMissingParamsList(query_holder, []string{"city_name", "iso_country", "iso_country", "country_name", "latitude", "longitude"})

	if len(missing) > 0 {
		MarshalPrintResponse(500, "The following parameters are missing: "+strings.Join(missing, ", "), make(map[string]string), w)
	} else {
		locationEntry := locations.CreateLocation(
			query_holder.Get("city_name"),
			query_holder.Get("iso_country"),
			query_holder.Get("country_name"),
			query_holder.Get("latitude"),
			query_holder.Get("longitude"),
			query_holder.Get("accuweather_id"),
			query_holder.Get("accuweather_city_name"),
			query_holder.Get("gismeteo_id"),
			query_holder.Get("gismeteo_city_name"))
		PrintLocationEntry(locationEntry, w)
	}
}

func ReadLocations(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintLocations(w)
}

func UpdateLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	missing := MakeMissingParamsList(query_holder, []string{"location_id", "city_name", "iso_country", "iso_country", "country_name", "latitude", "longitude"})
	if len(missing) > 0 {
		MarshalPrintResponse(500, "The following parameters are missing: "+strings.Join(missing, ", "), make(map[string]string), w)
	} else {
		locationEntry, _ := locations.UpdateLocation(
			query_holder.Get("location_id"),
			query_holder.Get("city_name"),
			query_holder.Get("iso_country"),
			query_holder.Get("country_name"),
			query_holder.Get("latitude"),
			query_holder.Get("longitude"),
			query_holder.Get("accuweather_id"),
			query_holder.Get("accuweather_city_name"),
			query_holder.Get("gismeteo_id"),
			query_holder.Get("gismeteo_city_name"))
		PrintLocationEntry(locationEntry, w)
	}
}

func UpsertLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	if len(query_holder.Get("location_id")) == 0 {
		CreateLocation(c, w, r)
	} else {
		UpdateLocation(c, w, r)
	}
}

func DeleteLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	missing := make([]string, 0)

	query_holder := r.URL.Query()

	MakeMissingParamsList(query_holder, []string{"location_id"})

	if len(missing) > 0 {
		MarshalPrintResponse(500, "The following parameters are missing: "+strings.Join(missing, ", "), make(map[string]string), w)
	} else {
		err := locations.DeleteLocation(query_holder.Get("location_id"))
		PrintStatus(err, "Location removed successfully.", w)
	}
}

func ClearLocations(c web.C, w http.ResponseWriter, r *http.Request) {
	err := locations.Clear()

	PrintStatus(err, "Locations cleared successfully.", w)
}

func ReadHistory(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()
	entryid := query_holder.Get("entryid")
	source := query_holder.Get("source")
	wtype := query_holder.Get("wtype")
	country := query_holder.Get("country")
	locationid := query_holder.Get("locationid")
	requeststart := query_holder.Get("requeststart")
	requestend := query_holder.Get("requestend")

	history_data := history.ReadHistory(entryid, source, wtype, country, locationid, requeststart, requestend)
	history_filters := map[string]string{"entryid": entryid, "source": source, "wtype": wtype, "country": country, "locationid": locationid, "requeststart": requeststart, "requestend": requestend}

	MarshalPrintResponse(200, "OK", map[string]interface{}{"history": map[string]interface{}{"data": history_data, "filters": history_filters}}, w)
}

func RefreshHistory(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	var wtypes []string
	wtype := query_holder.Get("wtype")

	if wtype == "" {
		wtypes = []string{"current", "forecast"}
	} else if wtype != "current" && wtype != "forecast" && wtype != "" {
		PrintStatus(errors.New(""), "Invalid wtype specified.", w)
		return
	} else {
		wtypes = []string{wtype}
	}

	locations_query := locations.ReadLocations()
	historyEntry := history.CreateHistoryEntry(locations_query, sources, wtypes)
	PrintHistoryEntry(historyEntry, w)
}

func ClearHistory(c web.C, w http.ResponseWriter, r *http.Request) {
	err := history.Clear()

	PrintStatus(err, "History cleared successfully.", w)
}

func Api(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func CheckApiKey(c web.C, w http.ResponseWriter, r *http.Request, adminKey string, cb func(web.C, http.ResponseWriter, *http.Request)) {
	key := r.URL.Query().Get("appid")

	if key == adminKey {
		cb(c, w, r)
	} else {
		InvalidApiKey(c, w, r)
	}
}

func GetPath(c web.C, w http.ResponseWriter, r *http.Request) {
	assetPath := "data" + r.URL.Path
	asset, err := bindata.Asset(assetPath)
	if err == nil {
		w.Header().Set("Content-Type", "text/html")
		fmt.Fprintf(w, string(asset))
	} else {
		fmt.Fprintf(w, err.Error()+"\n")
	}
}

func init() {
	flag.StringVar(&mongoDsn, "mongo", "mongodb://127.0.0.1:27017/weatherchecker", "MongoDB DSN")
}

func main() {
	flag.Parse()

	if os.Getenv("MONGO") != "" {
		mongoDsn = os.Getenv("MONGO")
	}

	var adminKey = os.Getenv("ADMIN_PASS")

	var closedForPublic bool
	if os.Getenv("CLOSED_FOR_PUBLIC") == "1" {
		closedForPublic = true
	}

	fmt.Println("Connecting to MongoDB at", mongoDsn)
	err := db_instance.Connect(mongoDsn)
	if err != nil {
		fmt.Println(fmt.Sprintf("Database error: %s", err))
		return
	}
	defer db_instance.Disconnect()

	const ApiVer = "0.1"

	const ApiEntrypoint = "/api" + "/" + ApiVer

	const SourcesEntrypoint = ApiEntrypoint + "/sources"
	const LocationEntrypoint = ApiEntrypoint + "/locations"
	const HistoryEntrypoint = ApiEntrypoint + "/history"

	const UIEntrypoint = "/ui"
	const UIPage = UIEntrypoint + "/index.html"

	goji.Use(Api)
	goji.Get(UIEntrypoint+"/*", GetPath)

	goji.Get(SourcesEntrypoint, ReadSources)
	goji.Get(LocationEntrypoint, ReadLocations)
	goji.Get(HistoryEntrypoint, ReadHistory)
	if !closedForPublic {
		goji.Get(ApiEntrypoint+"/check_appid", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, r.URL.Query().Get("appid"), ValidApiKey)
		})
		goji.Get(HistoryEntrypoint+"/refresh", RefreshHistory)
		goji.Get(LocationEntrypoint+"/add", CreateLocation)
		goji.Get(LocationEntrypoint+"/edit", UpdateLocation)
		goji.Get(LocationEntrypoint+"/upsert", UpsertLocation)
		goji.Get(LocationEntrypoint+"/remove", DeleteLocation)
		goji.Get(LocationEntrypoint+"/clear", ClearLocations)
		goji.Get(HistoryEntrypoint+"/clear", ClearHistory)
	} else {
		goji.Get(ApiEntrypoint+"/check_appid", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, ValidApiKey) })
		goji.Get(HistoryEntrypoint+"/refresh", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, RefreshHistory) })
		goji.Get(LocationEntrypoint+"/add", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, CreateLocation) })
		goji.Get(LocationEntrypoint+"/edit", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, UpdateLocation) })
		goji.Get(LocationEntrypoint+"/upsert", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, UpsertLocation) })
		goji.Get(LocationEntrypoint+"/remove", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, DeleteLocation) })
		goji.Get(LocationEntrypoint+"/clear", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, ClearLocations) })
		goji.Get(HistoryEntrypoint+"/clear", func(c web.C, w http.ResponseWriter, r *http.Request) { CheckApiKey(c, w, r, adminKey, ClearHistory) })
	}

	goji.Get(UIEntrypoint, http.RedirectHandler(UIPage, 301))
	goji.Get(UIEntrypoint+"/", http.RedirectHandler(UIPage, 301))
	goji.Get("/", http.RedirectHandler(UIPage, 301))
	goji.Serve()
}
<commit_msg>Sanitize API output<commit_after>package main

//go:generate go-bindata -o "bindata/bindata.go" -pkg "bindata" "data/..."
import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"

	"github.com/owm-inc/weatherchecker-go/bindata"
	"github.com/owm-inc/weatherchecker-go/db"
	"github.com/owm-inc/weatherchecker-go/structs"
)

type JsonResponse struct {
	Status  int         `json:"status"`
	Message string      `json:"message"`
	Content interface{} `json:"content"`
}

var sources = structs.CreateSources()

var mongoDsn string

var db_instance = db.Db()
var locations = structs.NewLocationTable(db_instance)
var history = structs.NewWeatherHistory(db_instance)

func MarshalPrintStuff(stuff interface{}, w http.ResponseWriter) {
	data, _ := json.Marshal(stuff)
	jsonString := string(data)
	fmt.Fprintf(w, jsonString)
}

func MarshalPrintResponse(status int, message string, content interface{}, w http.ResponseWriter) {
	MarshalPrintStuff(JsonResponse{Status: status, Message: message, Content: content}, w)
}

func PrintLocationEntry(locationEntry structs.LocationEntry, w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.LocationEntry{"location_entry": []structs.LocationEntry{locationEntry}}, w)
}

func PrintHistoryEntry(historyEntry []structs.HistoryDataEntry, w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.HistoryDataEntry{"history_entry": historyEntry}, w)
}

func PrintLocations(w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.LocationEntry{"locations": locations.ReadLocations()}, w)
}

func PrintSources(w http.ResponseWriter) {
	MarshalPrintResponse(200, "OK", map[string][]structs.SourceEntry{"sources": sources}, w)
}

func PrintSanitizedSources(w http.ResponseWriter) {
	output := make([]map[string]interface{}, len(sources))
	for i, source := range sources {
		entry := SanitizeSource(source)
		output[i] = entry
	}
	MarshalPrintResponse(200, "OK", map[string]interface{}{"sources": output}, w)
}

func PrintStatus(err error, successMessage string, w http.ResponseWriter) {
	var status int
	var message string

	if err != nil {
		status = 500
		message = err.Error()
	} else {
		status = 200
		message = successMessage
	}

	MarshalPrintResponse(status, message, make(map[string]string), w)
}

func NotAllowedForPublic(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintStatus(errors.New("This entrypoint has been disabled for public installation of Weather Checker."), "", w)
}

func InvalidApiKey(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintStatus(errors.New("Invalid API key."), "", w)
}

func ValidApiKey(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintStatus(nil, "API key valid.", w)
}

func SanitizeSource(source structs.SourceEntry) map[string]interface{} {
	entry := make(map[string]interface{})
	entry["name"] = source.Name
	entry["urls"] = source.Urls

	return entry
}

func MakeMissingParamsList(query_holder url.Values, required_params []string) (missing []string) {
	for _, entry := range required_params {
		if query_holder.Get(entry) == "" {
			missing = append(missing, entry)
		}
	}

	return missing
}

func ReadSources(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintSources(w)
}

func ReadSanitizedSources(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintSanitizedSources(w)
}

func CreateLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	missing := MakeMissingParamsList(query_holder, []string{"city_name", "iso_country", "iso_country", "country_name", "latitude", "longitude"})

	if len(missing) > 0 {
		MarshalPrintResponse(500, "The following parameters are missing: "+strings.Join(missing, ", "), make(map[string]string), w)
	} else {
		locationEntry := locations.CreateLocation(
			query_holder.Get("city_name"),
			query_holder.Get("iso_country"),
			query_holder.Get("country_name"),
			query_holder.Get("latitude"),
			query_holder.Get("longitude"),
			query_holder.Get("accuweather_id"),
			query_holder.Get("accuweather_city_name"),
			query_holder.Get("gismeteo_id"),
			query_holder.Get("gismeteo_city_name"))
		PrintLocationEntry(locationEntry, w)
	}
}

func ReadLocations(c web.C, w http.ResponseWriter, r *http.Request) {
	PrintLocations(w)
}

func UpdateLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	missing := MakeMissingParamsList(query_holder, []string{"location_id", "city_name", "iso_country", "iso_country", "country_name", "latitude", "longitude"})
	if len(missing) > 0 {
		MarshalPrintResponse(500, "The following parameters are missing: "+strings.Join(missing, ", "), make(map[string]string), w)
	} else {
		locationEntry, _ := locations.UpdateLocation(
			query_holder.Get("location_id"),
			query_holder.Get("city_name"),
			query_holder.Get("iso_country"),
			query_holder.Get("country_name"),
			query_holder.Get("latitude"),
			query_holder.Get("longitude"),
			query_holder.Get("accuweather_id"),
			query_holder.Get("accuweather_city_name"),
			query_holder.Get("gismeteo_id"),
			query_holder.Get("gismeteo_city_name"))
		PrintLocationEntry(locationEntry, w)
	}
}

func UpsertLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	if len(query_holder.Get("location_id")) == 0 {
		CreateLocation(c, w, r)
	} else {
		UpdateLocation(c, w, r)
	}
}

func DeleteLocation(c web.C, w http.ResponseWriter, r *http.Request) {
	missing := make([]string, 0)

	query_holder := r.URL.Query()

	MakeMissingParamsList(query_holder, []string{"location_id"})

	if len(missing) > 0 {
		MarshalPrintResponse(500, "The following parameters are missing: "+strings.Join(missing, ", "), make(map[string]string), w)
	} else {
		err := locations.DeleteLocation(query_holder.Get("location_id"))
		PrintStatus(err, "Location removed successfully.", w)
	}
}

func ClearLocations(c web.C, w http.ResponseWriter, r *http.Request) {
	err := locations.Clear()

	PrintStatus(err, "Locations cleared successfully.", w)
}

func ReadHistory(c web.C, w http.ResponseWriter, r *http.Request, sanitize bool) {
	query_holder := r.URL.Query()
	entryid := query_holder.Get("entryid")
	source := query_holder.Get("source")
	wtype := query_holder.Get("wtype")
	country := query_holder.Get("country")
	locationid := query_holder.Get("locationid")
	requeststart := query_holder.Get("requeststart")
	requestend := query_holder.Get("requestend")

	history_data := history.ReadHistory(entryid, source, wtype, country, locationid, requeststart, requestend)
	history_filters := map[string]string{"entryid": entryid, "source": source, "wtype": wtype, "country": country, "locationid": locationid, "requeststart": requeststart, "requestend": requestend}

	output := make([]interface{}, len(history_data))
	if sanitize {
		for i, history_entry := range history_data {
			entry := make(map[string]interface{})
			entry["objectid"] = history_entry.Id
			entry["status"] = history_entry.Status
			entry["location"] = history_entry.Location
			entry["source"] = SanitizeSource(history_entry.Source)
			entry["measurements"] = history_entry.Measurements
			entry["request_time"] = history_entry.RequestTime
			entry["wtype"] = history_entry.WType

			output[i] = entry
		}
	} else {
		for i, history_entry := range history_data {
			output[i] = history_entry
		}
	}

	MarshalPrintResponse(200, "OK", map[string]interface{}{"history": map[string]interface{}{"data": output, "filters": history_filters}}, w)
}

func ReadFullHistory(c web.C, w http.ResponseWriter, r *http.Request) {
	ReadHistory(c, w, r, false)
}

func ReadSanitizedHistory(c web.C, w http.ResponseWriter, r *http.Request) {
	ReadHistory(c, w, r, true)
}

func RefreshHistory(c web.C, w http.ResponseWriter, r *http.Request) {
	query_holder := r.URL.Query()

	var wtypes []string
	wtype := query_holder.Get("wtype")

	if wtype == "" {
		wtypes = []string{"current", "forecast"}
	} else if wtype != "current" && wtype != "forecast" && wtype != "" {
		PrintStatus(errors.New(""), "Invalid wtype specified.", w)
		return
	} else {
		wtypes = []string{wtype}
	}

	locations_query := locations.ReadLocations()
	historyEntry := history.CreateHistoryEntry(locations_query, sources, wtypes)
	PrintHistoryEntry(historyEntry, w)
}

func ClearHistory(c web.C, w http.ResponseWriter, r *http.Request) {
	err := history.Clear()

	PrintStatus(err, "History cleared successfully.", w)
}

func Api(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func CheckApiKey(c web.C, w http.ResponseWriter, r *http.Request, adminKey string, cb_success, cb_fail func(web.C, http.ResponseWriter, *http.Request)) {
	key := r.URL.Query().Get("appid")

	if key == adminKey {
		cb_success(c, w, r)
	} else {
		cb_fail(c, w, r)
	}
}

func GetPath(c web.C, w http.ResponseWriter, r *http.Request) {
	assetPath := "data" + r.URL.Path
	asset, err := bindata.Asset(assetPath)
	if err == nil {
		w.Header().Set("Content-Type", "text/html")
		fmt.Fprintf(w, string(asset))
	} else {
		fmt.Fprintf(w, err.Error()+"\n")
	}
}

func init() {
	flag.StringVar(&mongoDsn, "mongo", "mongodb://127.0.0.1:27017/weatherchecker", "MongoDB DSN")
}

func main() {
	flag.Parse()

	if os.Getenv("MONGO") != "" {
		mongoDsn = os.Getenv("MONGO")
	}

	var adminKey = os.Getenv("ADMIN_PASS")

	var closedForPublic bool
	if os.Getenv("CLOSED_FOR_PUBLIC") == "1" {
		closedForPublic = true
	}

	fmt.Println("Connecting to MongoDB at", mongoDsn)
	err := db_instance.Connect(mongoDsn)
	if err != nil {
		fmt.Println(fmt.Sprintf("Database error: %s", err))
		return
	}
	defer db_instance.Disconnect()

	const ApiVer = "0.1"

	const ApiEntrypoint = "/api" + "/" + ApiVer

	const SourcesEntrypoint = ApiEntrypoint + "/sources"
	const LocationEntrypoint = ApiEntrypoint + "/locations"
	const HistoryEntrypoint = ApiEntrypoint + "/history"

	const UIEntrypoint = "/ui"
	const UIPage = UIEntrypoint + "/index.html"

	goji.Use(Api)
	goji.Get(UIEntrypoint+"/*", GetPath)

	goji.Get(LocationEntrypoint, ReadLocations)
	if !closedForPublic {
		goji.Get(HistoryEntrypoint, ReadFullHistory)
		goji.Get(SourcesEntrypoint, ReadSources)
		goji.Get(ApiEntrypoint+"/check_appid", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, r.URL.Query().Get("appid"), ValidApiKey, InvalidApiKey)
		})
		goji.Get(HistoryEntrypoint+"/refresh", RefreshHistory)
		goji.Get(LocationEntrypoint+"/add", CreateLocation)
		goji.Get(LocationEntrypoint+"/edit", UpdateLocation)
		goji.Get(LocationEntrypoint+"/upsert", UpsertLocation)
		goji.Get(LocationEntrypoint+"/remove", DeleteLocation)
		goji.Get(LocationEntrypoint+"/clear", ClearLocations)
		goji.Get(HistoryEntrypoint+"/clear", ClearHistory)
	} else {
		goji.Get(HistoryEntrypoint, func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, ReadFullHistory, ReadSanitizedHistory)
		})
		goji.Get(SourcesEntrypoint, func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, ReadSources, ReadSanitizedSources)
		})
		goji.Get(ApiEntrypoint+"/check_appid", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, ValidApiKey, InvalidApiKey)
		})
		goji.Get(HistoryEntrypoint+"/refresh", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, RefreshHistory, InvalidApiKey)
		})
		goji.Get(LocationEntrypoint+"/add", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, CreateLocation, InvalidApiKey)
		})
		goji.Get(LocationEntrypoint+"/edit", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, UpdateLocation, InvalidApiKey)
		})
		goji.Get(LocationEntrypoint+"/upsert", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, UpsertLocation, InvalidApiKey)
		})
		goji.Get(LocationEntrypoint+"/remove", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, DeleteLocation, InvalidApiKey)
		})
		goji.Get(LocationEntrypoint+"/clear", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, ClearLocations, InvalidApiKey)
		})
		goji.Get(HistoryEntrypoint+"/clear", func(c web.C, w http.ResponseWriter, r *http.Request) {
			CheckApiKey(c, w, r, adminKey, ClearHistory, InvalidApiKey)
		})
	}

	goji.Get(UIEntrypoint, http.RedirectHandler(UIPage, 301))
	goji.Get(UIEntrypoint+"/", http.RedirectHandler(UIPage, 301))
	goji.Get("/", http.RedirectHandler(UIPage, 301))
	goji.Serve()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"log/syslog"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"net"

	"strings"

	"golang.org/x/build/kubernetes/api"
)

// Stream : structure for holding the stream of data coming from OpenShift
type Stream struct {
	Type  string    `json:"type,omitempty"`
	Event api.Event `json:"object"`
}

func main() {
	apiAddr := os.Getenv("OPENSHIFT_API_URL")
	apiToken := os.Getenv("OPENSHIFT_TOKEN")
	syslogServer := os.Getenv("SYSLOG_SERVER")
	syslogProto := strings.ToLower(os.Getenv("SYSLOG_PROTO"))
	syslogTag := strings.ToUpper(os.Getenv("SYSLOG_TAG"))
	ignoreSSL := strings.ToUpper(os.Getenv("IGNORE_SSL"))
	debugFlag := strings.ToUpper(os.Getenv("DEBUG"))

	// enable signal trapping
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c,
			syscall.SIGINT,  // Ctrl+C
			syscall.SIGTERM, // Termination Request
			syscall.SIGSEGV, // FullDerp
			syscall.SIGABRT, // Abnormal termination
			syscall.SIGILL,  // illegal instruction
			syscall.SIGFPE)  // floating point
		sig := <-c
		log.Fatalf("Signal (%v) Detected, Shutting Down", sig)
	}()

	// check and make sure we have the minimum config information before continuing
	if apiAddr == "" {
		// use the default internal cluster URL if not defined
		apiAddr = "https://openshift.default.svc.cluster.local"
		ignoreSSL = "TRUE"
		log.Print("Missing environment variable OPENSHIFT_API_URL. Using default API URL")
	}
	if apiToken == "" {
		// if we dont set it in the environment variable, read it out of
		// /var/run/secrets/kubernetes.io/serviceaccount/token
		log.Print("Missing environment variable OPENSHIFT_TOKEN. Leveraging serviceaccount token")
		fileData, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
		if err != nil {
			log.Fatal("Service Account token does not exist.")
		}
		apiToken = string(fileData)
	}
	if syslogTag == "" {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		syslogTag = "OSE"
	}
	if ignoreSSL == "" {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		ignoreSSL = "FALSE"
	}
	if debugFlag == "" {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		debugFlag = "FALSE"
	}
	if (syslogProto == "") || (syslogProto == "tcp") || (syslogProto == "udp") {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		if syslogProto == "" {
			syslogProto = "udp"
		} else {
			log.Printf("Will use %s for syslog protocol", syslogProto)
		}

	} else {
		log.Fatalf("SYSLOG_PROTO must be either blank, or tcp or udp not %s", syslogProto)
	}

	// Setup syslog connection only if syslogServer is defined
	if syslogServer != "" {
		sysLog, err := syslog.Dial(syslogProto, syslogServer,
			syslog.LOG_WARNING|syslog.LOG_DAEMON, syslogTag)
		if err != nil {
			log.Printf("Error connecting to %s", syslogServer)
			log.Fatal(err)
		} else {
			log.Printf("Event Forwarder configured to send all events to %s using tag %s", syslogServer, syslogTag)
			if debugFlag == "TRUE" {
				// dump the data to stdout AND syslog for testing.
				log.SetOutput(io.MultiWriter(sysLog, os.Stdout))
				ipAddr, _ := net.LookupHost(syslogServer)
				log.Printf("Connecting to IP address: %v\n", ipAddr)
			} else {
				log.SetOutput(sysLog)
			}
		}
	} else {
		log.Print("SYSLOG_SERVER environment variable not set. Sending all output to console.")
	}

	// setup ose connection
	var client http.Client
	if ignoreSSL == "TRUE" {
		tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
		client = http.Client{Transport: tr}
	} else {
		client = http.Client{}
	}
	req, err := http.NewRequest("GET", apiAddr+"/api/v1/events?watch=true", nil)
	if err != nil {
		log.Fatal("## Error while opening connection to openshift api", err)
	}
	req.Header.Add("Authorization", "Bearer "+apiToken)

	// Start out with only never events than now
	lastLogged := time.Now()

	for {
		resp, err := client.Do(req)

		if err != nil {
			log.Println("## Error while connecting to:", apiAddr, err)
			time.Sleep(5 * time.Second)
			continue
		}

		reader := bufio.NewReader(resp.Body)

		for {
			line, err := reader.ReadBytes('\n')
			if err != nil {
				log.Println("## Error reading from response stream.", err, line)
				resp.Body.Close()
				break
			}

			event := Stream{}
			decErr := json.Unmarshal(line, &event)
			if decErr != nil {
				log.Println("## Error decoding json.", err)
				resp.Body.Close()
				break
			}

			// Kube API reports all logs in OpenShift, we only want the new ones
			if event.Event.LastTimestamp.After(lastLogged) {
				lastLogged = event.Event.LastTimestamp.Time
				fmt.Printf("%v | Project: %v | Name: %v | Kind: %v | Reason: %v | Message: %v\n",
					event.Event.LastTimestamp.Format(time.RFC3339),
					event.Event.Namespace, event.Event.Name,
					event.Event.Kind, event.Event.Reason, event.Event.Message)
			}
		}
	}
}
<commit_msg>Bugfixes with lastlog.<commit_after>package main

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"log/syslog"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"net"

	"strings"

	"golang.org/x/build/kubernetes/api"
)

// Stream : structure for holding the stream of data coming from OpenShift
type Stream struct {
	Type  string    `json:"type,omitempty"`
	Event api.Event `json:"object"`
}

func main() {
	apiAddr := os.Getenv("OPENSHIFT_API_URL")
	apiToken := os.Getenv("OPENSHIFT_TOKEN")
	syslogServer := os.Getenv("SYSLOG_SERVER")
	syslogProto := strings.ToLower(os.Getenv("SYSLOG_PROTO"))
	syslogTag := strings.ToUpper(os.Getenv("SYSLOG_TAG"))
	ignoreSSL := strings.ToUpper(os.Getenv("IGNORE_SSL"))
	debugFlag := strings.ToUpper(os.Getenv("DEBUG"))

	// enable signal trapping
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c,
			syscall.SIGINT,  // Ctrl+C
			syscall.SIGTERM, // Termination Request
			syscall.SIGSEGV, // FullDerp
			syscall.SIGABRT, // Abnormal termination
			syscall.SIGILL,  // illegal instruction
			syscall.SIGFPE)  // floating point
		sig := <-c
		log.Fatalf("Signal (%v) Detected, Shutting Down", sig)
	}()

	// check and make sure we have the minimum config information before continuing
	if apiAddr == "" {
		// use the default internal cluster URL if not defined
		apiAddr = "https://openshift.default.svc.cluster.local"
		ignoreSSL = "TRUE"
		log.Print("Missing environment variable OPENSHIFT_API_URL. Using default API URL")
	}
	if apiToken == "" {
		// if we dont set it in the environment variable, read it out of
		// /var/run/secrets/kubernetes.io/serviceaccount/token
		log.Print("Missing environment variable OPENSHIFT_TOKEN. Leveraging serviceaccount token")
		fileData, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
		if err != nil {
			log.Fatal("Service Account token does not exist.")
		}
		apiToken = string(fileData)
	}
	if syslogTag == "" {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		syslogTag = "OSE"
	}
	if ignoreSSL == "" {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		ignoreSSL = "FALSE"
	}
	if debugFlag == "" {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		debugFlag = "FALSE"
	}
	if (syslogProto == "") || (syslogProto == "tcp") || (syslogProto == "udp") {
		// we don't need to error out here, but we do need to set a default if the variable isn't defined
		if syslogProto == "" {
			syslogProto = "udp"
		} else {
			log.Printf("Will use %s for syslog protocol", syslogProto)
		}

	} else {
		log.Fatalf("SYSLOG_PROTO must be either blank, or tcp or udp not %s", syslogProto)
	}

	// Setup syslog connection only if syslogServer is defined
	if syslogServer != "" {
		sysLog, err := syslog.Dial(syslogProto, syslogServer,
			syslog.LOG_WARNING|syslog.LOG_DAEMON, syslogTag)
		if err != nil {
			log.Printf("Error connecting to %s", syslogServer)
			log.Fatal(err)
		} else {
			log.Printf("Event Forwarder configured to send all events to %s using tag %s", syslogServer, syslogTag)
			if debugFlag == "TRUE" {
				// dump the data to stdout AND syslog for testing.
				log.SetOutput(io.MultiWriter(sysLog, os.Stdout))
				ipAddr, _ := net.LookupHost(syslogServer)
				log.Printf("Connecting to IP address: %v\n", ipAddr)
			} else {
				log.SetOutput(sysLog)
			}
		}
	} else {
		log.Print("SYSLOG_SERVER environment variable not set. Sending all output to console.")
	}

	// setup ose connection
	var client http.Client
	if ignoreSSL == "TRUE" {
		tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
		client = http.Client{Transport: tr}
	} else {
		client = http.Client{}
	}
	req, err := http.NewRequest("GET", apiAddr+"/api/v1/events?watch=true", nil)
	if err != nil {
		log.Fatal("## Error while opening connection to openshift api", err)
	}
	req.Header.Add("Authorization", "Bearer "+apiToken)

	for {
		resp, err := client.Do(req)

		if err != nil {
			log.Println("## Error while connecting to:", apiAddr, err)
			time.Sleep(5 * time.Second)
			continue
		}

		streamStart := time.Now()
		reader := bufio.NewReader(resp.Body)

		for {
			line, err := reader.ReadBytes('\n')
			if err != nil {
				log.Println("## Error reading from response stream.", err, line)
				resp.Body.Close()
				break
			}

			event := Stream{}
			decErr := json.Unmarshal(line, &event)
			if decErr != nil {
				log.Println("## Error decoding json.", err)
				resp.Body.Close()
				break
			}

			// Kubernetes sends all data from ETCD, we only want the logs since the stream started
			if event.Event.LastTimestamp.Time.After(streamStart) {
				fmt.Printf("%v | Project: %v | Name: %v | Kind: %v | Reason: %v | Message: %v\n",
					event.Event.LastTimestamp.Format(time.RFC3339),
					event.Event.Namespace, event.Event.Name,
					event.Event.Kind, event.Event.Reason, event.Event.Message)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
)

func main() {
	logger := log.New(os.Stdout, "", log.Lshortfile)
	logger.Println("Hello")
}
<commit_msg>Add basic docopt<commit_after>package main

import (
	"log"
	"os"

	"github.com/docopt/docopt-go"
)

func main() {
	usage := `ury-listd-go.

Usage:
  ury-listd-go [-p <port>] [-a <address>] [-P <port>] [-A <address>]
  ury-listd-go -h
  ury-listd-go -v

Options:
  -p --port=<port>              The port ury-listd-go listens on [default: 1351].
  -a --addr=<address>           The host ury-listd-go listens on [default: 127.0.0.1].
  -P --playoutport=<port>       The playout system's listening port [default: 1350].
  -A --playoutaddr=<address>    The playout system's listening address [default: 127.0.0.1].
  -h --help                     Show this screen.
  -v --version                  Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, "ury-listd-go 0.0", false)
	logger := log.New(os.Stdout, "", log.Lshortfile)
	logger.Println(arguments)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"net"
	"strings"

	"github.com/orivej/e"
	"github.com/pkg/errors"
)

type ReadResult struct {
	n   int
	err error
	buf []byte
}

const (
	bufSize         = 4000
	clientTLSMarker = "<starttls"
	serverTLSMarker = "<proceed"
)

var (
	flListenPort = flag.Int("port", 5222, "listen port")
	flServer     = flag.String("server", "<server:port>", "target server:port")

	flKeyPath  = flag.String("key", "", "path to TLS certificate key")
	flCertPath = flag.String("cert", "", "path to TLS certificate")

	flReplaceLocal  = flag.String("replace-local", "", "")
	flReplaceRemote = flag.String("replace-remote", "", "")
)

func main() {
	flag.Parse()

	certificate, err := tls.X509KeyPair([]byte(localhostCert), []byte(localhostKey))
	e.Exit(err)
	if len(*flCertPath) > 0 {
		certificate, err = tls.LoadX509KeyPair(*flCertPath, *flKeyPath)
		e.Exit(errors.Wrap(err, "can not load TLS key pair"))
	}

	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *flListenPort))
	e.Exit(err)

	for idx := 1; true; idx++ {
		client, err := listener.Accept()
		e.Exit(err)
		go func(idx int) {
			defer e.CloseOrPrint(client)
			err := serve(client, idx, certificate)
			fmt.Printf("X%d: %s\n", idx, err)
		}(idx)
	}
}

func serve(client net.Conn, idx int, certificate tls.Certificate) error {
	server, err := net.Dial("tcp", *flServer)
	if err != nil {
		return errors.Wrap(err, "failed to connect to server")
	}
	defer e.CloseOrPrint(server)

	err = proxy(client, server, idx, true)
	if err != nil {
		return errors.Wrap(err, "session terminated")
	}

	tlsClient := tls.Server(client, &tls.Config{
		Certificates: []tls.Certificate{certificate},
	})
	tlsServer := tls.Client(server, &tls.Config{
		InsecureSkipVerify: true,
	})
	fmt.Printf("X%d: eavesdropping TLS\n", idx)
	err = proxy(tlsClient, tlsServer, idx, false)
	return errors.Wrap(err, "TLS session terminated")
}

func proxy(client, server io.ReadWriter, idx int, untilTLS bool) error {
	clientNext, clientResults := startReader(client)
	serverNext, serverResults := startReader(server)

	rLocal := []byte(*flReplaceLocal)
	rRemote := []byte(*flReplaceRemote)
	replace := len(rLocal) > 0 && len(rRemote) > 0

	for {
		select {
		case result := <-clientResults:
			err := result.err
			if err != nil {
				return errors.Wrap(err, "failed to read from client")
			}

			buf := result.buf[:result.n]
			if replace {
				buf = bytes.Replace(buf, rLocal, rRemote, -1)
			}
			fmt.Printf("C%d|%s|\n", idx, escape(string(buf)))

			_, err = server.Write(buf)
			if err != nil {
				return errors.Wrap(err, "failed to write to server")
			}

			if untilTLS && bytes.Contains(buf, []byte(clientTLSMarker)) {
				fmt.Printf("X%d: client goes TLS\n", idx)
				clientNext <- false
				continue
			}

			clientNext <- true

		case result := <-serverResults:
			err := result.err
			if err != nil {
				return errors.Wrap(err, "failed to read from server")
			}

			buf := result.buf[:result.n]
			fmt.Printf("S%d|%s|\n", idx, escape(string(buf)))
			if replace {
				buf = bytes.Replace(buf, rRemote, rLocal, -1)
			}

			_, err = client.Write(buf)
			if err != nil {
				return errors.Wrap(err, "failed to write to client")
			}

			if untilTLS && bytes.Contains(buf, []byte(serverTLSMarker)) {
				fmt.Printf("X%d: server goes TLS\n", idx)
				serverNext <- false
				return nil
			}

			serverNext <- true
		}
	}
}

func startReader(reader io.Reader) (chan<- bool, chan ReadResult) {
	readNext := make(chan bool)
	buf := make([]byte, bufSize)
	results := make(chan ReadResult)
	go func() {
		for {
			n, err := reader.Read(buf)
			results <- ReadResult{n, err, buf}
			if !<-readNext {
				return
			}
		}
	}()
	return readNext, results
}

func escape(s string) string {
	s = strings.Replace(s, "\\", "\\\\", -1)
	s = strings.Replace(s, "\n", "\\n", -1)
	return s
}
<commit_msg>Add -log-dir option<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/orivej/e"
	"github.com/pkg/errors"
)

type ReadResult struct {
	n   int
	err error
	buf []byte
}

type Log func(side, data string)

const (
	bufSize         = 4000
	clientTLSMarker = "<starttls"
	serverTLSMarker = "<proceed"
)

var (
	flListenPort = flag.Int("port", 5222, "listen port")
	flServer     = flag.String("server", "<server:port>", "target server:port")
	flLogDir     = flag.String("log-dir", "", "directory for session logs")

	flKeyPath  = flag.String("key", "", "path to TLS certificate key")
	flCertPath = flag.String("cert", "", "path to TLS certificate")

	flReplaceLocal  = flag.String("replace-local", "", "")
	flReplaceRemote = flag.String("replace-remote", "", "")
)

func main() {
	flag.Parse()

	certificate, err := tls.X509KeyPair([]byte(localhostCert), []byte(localhostKey))
	e.Exit(err)
	if len(*flCertPath) > 0 {
		certificate, err = tls.LoadX509KeyPair(*flCertPath, *flKeyPath)
		e.Exit(errors.Wrap(err, "can not load TLS key pair"))
	}

	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *flListenPort))
	e.Exit(err)

	for idx := nextIdx(); true; idx++ {
		idx := idx
		client, err := listener.Accept()
		e.Exit(err)

		var f *os.File
		if *flLogDir != "" {
			path := filepath.Join(*flLogDir, logName(idx))
			flags := os.O_WRONLY | os.O_CREATE | os.O_EXCL
			f, err = os.OpenFile(path, flags, 0666)
			e.Exit(err)
		}
		pr := func(side, data string) {
			ts := time.Now().Format(time.RFC3339)
			fmt.Printf("%04d %s %s %s\n", idx, ts, side, data)
			if f != nil {
				fmt.Fprintf(f, "%s %s %s\n", ts, side, data)
			}
		}

		go func() {
			defer e.CloseOrPrint(client)
			err := serve(client, pr, certificate)
			pr("X", err.Error())
		}()
	}
}

func nextIdx() int {
	if *flLogDir == "" {
		return 1
	}

	files, err := ioutil.ReadDir(*flLogDir)
	e.Exit(err)

	idx := 1
	for _, file := range files {
		if file.Name() == logName(idx) {
			idx++
		}
	}
	return idx
}

func logName(idx int) string {
	return fmt.Sprintf("%04d.log", idx)
}

func serve(client net.Conn, pr Log, certificate tls.Certificate) error {
	server, err := net.Dial("tcp", *flServer)
	if err != nil {
		return errors.Wrap(err, "failed to connect to server")
	}
	defer e.CloseOrPrint(server)

	err = proxy(client, server, pr, true)
	if err != nil {
		return errors.Wrap(err, "session terminated")
	}

	tlsClient := tls.Server(client, &tls.Config{
		Certificates: []tls.Certificate{certificate},
	})
	tlsServer := tls.Client(server, &tls.Config{
		InsecureSkipVerify: true,
	})
	pr("X", "eavesdropping TLS")
	err = proxy(tlsClient, tlsServer, pr, false)
	return errors.Wrap(err, "TLS session terminated")
}

func proxy(client, server io.ReadWriter, pr Log, untilTLS bool) error {
	clientNext, clientResults := startReader(client)
	serverNext, serverResults := startReader(server)

	rLocal := []byte(*flReplaceLocal)
	rRemote := []byte(*flReplaceRemote)
	replace := len(rLocal) > 0 && len(rRemote) > 0

	for {
		select {
		case result := <-clientResults:
			err := result.err
			if err != nil {
				return errors.Wrap(err, "failed to read from client")
			}

			buf := result.buf[:result.n]
			if replace {
				buf = bytes.Replace(buf, rLocal, rRemote, -1)
			}
			pr("?", escape(string(buf)))

			_, err = server.Write(buf)
			if err != nil {
				return errors.Wrap(err, "failed to write to server")
			}

			if untilTLS && bytes.Contains(buf, []byte(clientTLSMarker)) {
				pr("X", "client requests TLS")
				clientNext <- false
				continue
			}

			clientNext <- true

		case result := <-serverResults:
			err := result.err
			if err != nil {
				return errors.Wrap(err, "failed to read from server")
			}

			buf := result.buf[:result.n]
			pr("!", escape(string(buf)))
			if replace {
				buf = bytes.Replace(buf, rRemote, rLocal, -1)
			}

			_, err = client.Write(buf)
			if err != nil {
				return errors.Wrap(err, "failed to write to client")
			}

			if untilTLS && bytes.Contains(buf, []byte(serverTLSMarker)) {
				pr("X", "server approves TLS")
				serverNext <- false
				return nil
			}

			serverNext <- true
		}
	}
}

func startReader(reader io.Reader) (chan<- bool, chan ReadResult) {
	readNext := make(chan bool)
	buf := make([]byte, bufSize)
	results := make(chan ReadResult)
	go func() {
		for {
			n, err := reader.Read(buf)
			results <- ReadResult{n, err, buf}
			if !<-readNext {
				return
			}
		}
	}()
	return readNext, results
}

var escaper = strings.NewReplacer(
	"\\", "\\\\",
	"\n", "\\n",
	"\r", "\\r",
)

func escape(s string) string {
	return escaper.Replace(s)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"golang.org/x/net/html/charset"
	"io/ioutil"
	"log"
	"net/http"
	"os/exec"
	"regexp"
	// "syscall"
)

type songInfo struct {
	artist, title string
}

func main() {
	// TODO
	// exit status
	// strings vs bytes slices
	// http client : what are default settings?

	cmusStatus, err := getCmusStatus()
	if err != nil {
		log.Fatal(err)
	}

	songinfo, err := parseCmusStatus(cmusStatus)
	if err != nil {
		log.Fatal(err)
	}

	client := &http.Client{}

	lyrics, err := fetchLyrics(client, songinfo)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(lyrics))
}

func getCmusStatus() ([]byte, error) {
	cmd := exec.Command("cmus-remote", "-Q")
	return cmd.Output()
}

var artistRe = regexp.MustCompile(`(?m)^tag\s+artist\s+(.+)\s*$`)
var titleRe = regexp.MustCompile(`(?m)^tag\s+title\s+(.+)\s*$`)

func regexpMatch(re *regexp.Regexp, buf []byte) []byte {
	match := re.FindAllSubmatch(buf, 1)
	if len(match) > 0 {
		return match[0][1]
	}
	return nil
}

func parseCmusStatus(cmusStatus []byte) (*songInfo, error) {
	artist := regexpMatch(artistRe, cmusStatus)
	title := regexpMatch(titleRe, cmusStatus)

	if artist == nil || title == nil {
		return nil, errors.New("Failed to parse cmus status")
	}

	si := songInfo{
		artist: string(artist),
		title:  string(title),
	}

	return &si, nil
}

func makeURL(si *songInfo) string {
	artist := []byte(si.artist)
	title := []byte(si.title)

	theRe := regexp.MustCompile(`(?i)^the `)
	weirdRe := regexp.MustCompile(`(?i)[^a-z0-9]`)

	artist = theRe.ReplaceAll(artist, []byte{})

	artist = bytes.ToLower(artist)
	title = bytes.ToLower(title)

	for _, str := range []*[]byte{&artist, &title} {
		*str = bytes.ToLower(*str)
		*str = weirdRe.ReplaceAll(*str, []byte{})
	}

	url := "https://www.azlyrics.com/lyrics/"
	url += string(artist) + "/" + string(title) + ".html"

	return url
}

func fetchLyrics(client *http.Client, si *songInfo) ([]byte, error) {
	reqUrl := makeURL(si)

	req, err := http.NewRequest("GET", reqUrl, nil)
	if err != nil {
		return []byte{}, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return []byte{}, err
	}

	if resp.StatusCode != 200 {
		return []byte{}, errors.New(resp.Status)
	}

	utf8, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		return []byte{}, err
	}
	body, err := ioutil.ReadAll(utf8)
	if err != nil {
		return []byte{}, err
	}

	lyrics, err := parseLyrics(body)
	if err != nil {
		return []byte{}, err
	}

	return lyrics, nil
}

func htmlStrip(html []byte) []byte {
	commentsRe := regexp.MustCompile(`(?s)<!--.*?-->`)
	brRe := regexp.MustCompile(`<br/?>`)

	html = commentsRe.ReplaceAll(html, []byte{})
	html = brRe.ReplaceAll(html, []byte{})

	// TODO
	// trim leading|trainling whitespace for every line

	return html
}

func parseLyrics(lyricsHtml []byte) ([]byte, error) {
	re := regexp.MustCompile(
		`(?s)<div[^<>]*?class="lyricsh"[^<>]*?>.*?</div>\s*?` +
			`<div[^<>]*?>.*?</div>\s*` +
			`.*?` +
			`<div[^<>]*?>(.*?)</div>`)

	match := re.FindAllSubmatch(lyricsHtml, 1)
	if match == nil {
		return []byte{}, errors.New("Failed to parse html")
	}

	lyrics := htmlStrip(match[0][1])
	return lyrics, nil
}
<commit_msg>Create dirs unless exist<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"golang.org/x/net/html/charset"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
)

type songInfo struct {
	artist, title string
}

func main() {
	home := os.Getenv("HOME")
	if home == "" {
		log.Fatal("HOME not found")
	}

	dotDir := path.Join(home, ".show-lyrics")
	cacheDir := path.Join(dotDir, "cache")

	for _, dir := range []string{dotDir, cacheDir} {
		err := mkdirUnlessExists(dir)
		if err != nil {
			log.Fatal(err)
		}
	}

	cmusStatus, err := getCmusStatus()
	if err != nil {
		log.Fatal(err)
	}

	songinfo, err := parseCmusStatus(cmusStatus)
	if err != nil {
		log.Fatal(err)
	}

	client := &http.Client{}

	lyrics, err := fetchLyrics(client, songinfo)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(lyrics))
}

func mkdirUnlessExists(dir string) error {
	_, err := os.Stat(dir)
	if err != nil {
		err = os.Mkdir(dir, 0755)
		if err != nil {
			return err
		}
	}
	return nil
}

func getCmusStatus() ([]byte, error) {
	cmd := exec.Command("cmus-remote", "-Q")
	return cmd.Output()
}

var artistRe = regexp.MustCompile(`(?m)^tag\s+artist\s+(.+)\s*$`)
var titleRe = regexp.MustCompile(`(?m)^tag\s+title\s+(.+)\s*$`)

func regexpMatch(re *regexp.Regexp, buf []byte) []byte {
	match := re.FindAllSubmatch(buf, 1)
	if len(match) > 0 {
		return match[0][1]
	}
	return nil
}

func parseCmusStatus(cmusStatus []byte) (*songInfo, error) {
	artist := regexpMatch(artistRe, cmusStatus)
	title := regexpMatch(titleRe, cmusStatus)

	if artist == nil || title == nil {
		return nil, errors.New("Failed to parse cmus status")
	}

	si := songInfo{
		artist: string(artist),
		title:  string(title),
	}

	return &si, nil
}

func makeURL(si *songInfo) string {
	artist := []byte(si.artist)
	title := []byte(si.title)

	theRe := regexp.MustCompile(`(?i)^the `)
	weirdRe := regexp.MustCompile(`(?i)[^a-z0-9]`)

	artist = theRe.ReplaceAll(artist, []byte{})

	artist = bytes.ToLower(artist)
	title = bytes.ToLower(title)

	for _, str := range []*[]byte{&artist, &title} {
		*str = bytes.ToLower(*str)
		*str = weirdRe.ReplaceAll(*str, []byte{})
	}

	url := "https://www.azlyrics.com/lyrics/"
	url += string(artist) + "/" + string(title) + ".html"

	return url
}

func fetchLyrics(client *http.Client, si *songInfo) ([]byte, error) {
	reqUrl := makeURL(si)

	req, err := http.NewRequest("GET", reqUrl, nil)
	if err != nil {
		return []byte{}, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return []byte{}, err
	}

	if resp.StatusCode != 200 {
		return []byte{}, errors.New(resp.Status)
	}

	utf8, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		return []byte{}, err
	}
	body, err := ioutil.ReadAll(utf8)
	if err != nil {
		return []byte{}, err
	}

	lyrics, err := parseLyrics(body)
	if err != nil {
		return []byte{}, err
	}

	return lyrics, nil
}

func htmlStrip(html []byte) []byte {
	commentsRe := regexp.MustCompile(`(?s)<!--.*?-->`)
	brRe := regexp.MustCompile(`<br/?>`)

	html = commentsRe.ReplaceAll(html, []byte{})
	html = brRe.ReplaceAll(html, []byte{})

	// TODO
	// trim leading|trainling whitespace for every line

	return html
}

func parseLyrics(lyricsHtml []byte) ([]byte, error) {
	re := regexp.MustCompile(
		`(?s)<div[^<>]*?class="lyricsh"[^<>]*?>.*?</div>\s*?` +
			`<div[^<>]*?>.*?</div>\s*` +
			`.*?` +
			`<div[^<>]*?>(.*?)</div>`)

	match := re.FindAllSubmatch(lyricsHtml, 1)
	if match == nil {
		return []byte{}, errors.New("Failed to parse html")
	}

	lyrics := htmlStrip(match[0][1])
	return lyrics, nil
}
<|endoftext|>


<commit_before>package main

import (
  "net/http"
  "os"
  "os/exec"
  "fmt"
  "strings"
)

/*
 * POST /build/:host/:user/:name
 */
func JekyllBuild(rw http.ResponseWriter, r *http.Request) {
  // webhook must be POST
  if r.Method != "POST" {
    rw.WriteHeader(http.StatusNotFound)
    return
  }

  url := strings.Split(r.URL.Path, "/")
  host, user, name := url[2], url[3], url[4]
  tmp := "/tmp"

  dir  := tmp + "/src/" + name
  dest := tmp + "/build/" + name
  repo := "git@" + host + ":" + user + "/" + name + ".git"

  cmd := []string{
    "git clone %[2]s %[1]s &&",
    "cd %[1]s;",
    // "git checkout master",
    "[ -f Gemfile ] && bundle install;",
    "jekyll build -s %[1]s -d %[3]s;",
    "rm -Rf %[1]s;"}

  fmt.Println("-----> Cloning " + repo)
  fmt.Println("-----> Building Jekyll site ...")
  out, err := exec.Command("sh", "-c", fmt.Sprintf(strings.Join(cmd, " "), dir, repo, dest)).Output()
  if err != nil {
    fmt.Printf("%s", err)
    rw.WriteHeader(http.StatusInternalServerError)
    return
  }
  fmt.Printf("%s", out)
  fmt.Println("-----> Jekyll site built successfully.")

  status, message := JekyllPublish(dest)
  fmt.Println(message)

  rw.WriteHeader(status)
}

func JekyllPublish(dir string) (int, string) {

  // Find S3 Bucket location from the BUCKET file
  bucket, err := exec.Command("sh", "-c", fmt.Sprintf("cd %s && cat BUCKET", dir)).Output()
  if err != nil {
    return 500, "No bucket specified"
  }

  // Sync files to S3
  fmt.Printf("-----> Publishing to Amazon S3 Bucket %s...\n", bucket)
  out, err := exec.Command(
    "s3cmd",
    "sync",
    dir + "/",
    "s3://" + string(bucket),
    "--delete-removed",
    "--acl-public",
    "--add-header=Cache-Control:max-age=60",
  ).CombinedOutput()
  fmt.Printf("%s", out)
  if err != nil {
    fmt.Printf("%s", err)
    return 500, "Problem syncing"
  }

  // TODO Optional - Use rsync to remote web server
  // rsync -avz --delete _site/ location:/var/www/html/jekyll

  // remove build
  os.RemoveAll(dir)

  return 200, "Success"
}

func main() {
  http.HandleFunc("/build/", JekyllBuild)
  port := os.Getenv("PORT")
  if port == "" {
      port = "8080"
  }
  http.ListenAndServe(":"+port, nil)
}

<commit_msg>better error logging<commit_after>package main

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

/*
 * POST /build/:host/:user/:name
 */
func JekyllBuild(rw http.ResponseWriter, r *http.Request) {
  // webhook must be POST
  if r.Method != "POST" {
    rw.WriteHeader(http.StatusNotFound)
    return
  }

  url := strings.Split(r.URL.Path, "/")
  host, user, name := url[2], url[3], url[4]
  tmp := "/tmp"

  dir  := tmp + "/src/" + name
  dest := tmp + "/build/" + name
  repo := "git@" + host + ":" + user + "/" + name + ".git"

  cmd := []string{
    "git clone %[2]s %[1]s &&",
    "cd %[1]s;",
    // "git checkout master",
    "[ -f Gemfile ] && bundle install;",
    "jekyll build -s %[1]s -d %[3]s;",
    "rm -Rf %[1]s;"}

  log.Println("-----> Cloning " + repo)
  log.Println("-----> Building Jekyll site ...")
  out, err := exec.Command("sh", "-c", fmt.Sprintf(strings.Join(cmd, " "), dir, repo, dest)).Output()
  log.Println(string(out))
  if err != nil {
    log.Println("ERROR: %s", err)
    rw.WriteHeader(http.StatusInternalServerError)
    return
  }
  fmt.Println("-----> Jekyll site built successfully.")

  status, message := JekyllPublish(dest)
  fmt.Println(message)

  rw.WriteHeader(status)
}

func JekyllPublish(dir string) (int, string) {

  // Find S3 Bucket location from the BUCKET file
  bucket, err := exec.Command("sh", "-c", fmt.Sprintf("cd %s && cat BUCKET", dir)).Output()
  if err != nil {
    return 500, "No bucket specified"
  }

  // Sync files to S3
  fmt.Printf("-----> Publishing to Amazon S3 Bucket %s...\n", bucket)
  out, err := exec.Command(
    "s3cmd",
    "sync",
    dir + "/",
    "s3://" + string(bucket),
    "--delete-removed",
    "--acl-public",
    "--add-header=Cache-Control:max-age=60",
  ).CombinedOutput()
  fmt.Printf("%s", out)
  if err != nil {
    fmt.Printf("%s", err)
    return 500, "Problem syncing"
  }

  // TODO Optional - Use rsync to remote web server
  // rsync -avz --delete _site/ location:/var/www/html/jekyll

  // remove build
  os.RemoveAll(dir)

  return 200, "Success"
}

func main() {
  http.HandleFunc("/build/", JekyllBuild)
  port := os.Getenv("PORT")
  if port == "" {
      port = "8080"
  }
  http.ListenAndServe(":"+port, nil)
}

<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/plugin"
)

/*
*	This is the struct implementing the interface defined by the core CLI. It can
*	be found at  "github.com/cloudfoundry/cli/plugin/plugin.go"
*
 */
type FastPushPlugin struct {
	ui terminal.UI
}

/*
*	This function must be implemented by any plugin because it is part of the
*	plugin interface defined by the core CLI.
*
*	Run(....) is the entry point when the core CLI is invoking a command defined
*	by a plugin. The first parameter, plugin.CliConnection, is a struct that can
*	be used to invoke cli commands. The second paramter, args, is a slice of
*	strings. args[0] will be the name of the command, and will be followed by
*	any additional arguments a cli user typed in.
*
*	Any error handling should be handled with the plugin itself (this means printing
*	user facing errors). The CLI will exit 0 if the plugin exits 0 and will exit
*	1 should the plugin exits nonzero.
 */
func (c *FastPushPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	// Ensure that the user called the command fast-push
	// alias fp is auto mapped
	if args[0] != "fast-push" || args[0] != "fp" {
		return
	}

	c.ui = terminal.NewUI(os.Stdin, terminal.NewTeePrinter())

	if cliConnection.IsLoggedIn() == false {
		panic("cannot perform fast-push without being logged in to CF")
	}

	// set flag for dry run
	flagSet := flag.NewFlagSet("fpfs", flag.ExitOnError)
	dryRun := flagSet.Bool("dry", false, "dry run flag")

	err := flagSet.Parse(args[1:])
	if err != nil {
		c.ui.Failed(err.Error())
	}

	if len(args) > 2 {
		fmt.Println("Running the fast-push command")
		fmt.Printf("Target app: %s /n", args[1])
		// check if the user asked for a dry run or not
		if *c.dryRun {
			c.fastPush(cliConnection, args[1], true)
		} else {
			c.fastPush(cliConnection, args[1], false)
		}
	} else {
		c.showUsage(args)
	}

}

func (c *FastPushPlugin) fastPush(cliConnection plugin.CliConnection, appName string, dryRun bool) {
	// Please check what GetApp returns here
	// https://github.com/cloudfoundry/cli/blob/master/plugin/models/get_app.go
	var appUrls []string

	if dryRun {
		c.ui.Warn("warning: No changes will be applied, this is a dry run !!")
	}

	app := c.GetApp(appName)
	routes := app.Routes

	if len(routes) > 1 {
		for _, route := range routes {
			c.ui.Warn("multiple corresponding url's has been found")
			panic("NOT IMPLEMENTED YET!")
		}
	} else {
		c.ui.Say("corresponding app host: %s", routes[0].Host)
		c.ui.Say("corresponding app domain: %s", routes[0].Domain.Name)
	}

	// dispatch request TODO
	url := routes[0].Host
	var query = []byte(`query-here`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(query))
	req.Header.Set("X-Custom-Header", "somevalue")
	req.Header.Set("Content-Type", "text/plain")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	fmt.Println("response Status:", resp.Status)
	fmt.Println("response Headers:", resp.Header)
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))
}

/*
*	This function must be implemented as part of the plugin interface
*	defined by the core CLI.
*
*	GetMetadata() returns a PluginMetadata struct. The first field, Name,
*	determines the name of the plugin which should generally be without spaces.
*	If there are spaces in the name a user will need to properly quote the name
*	during uninstall otherwise the name will be treated as seperate arguments.
*	The second value is a slice of Command structs. Our slice only contains one
*	Command Struct, but could contain any number of them. The first field Name
*	defines the command `cf basic-plugin-command` once installed into the CLI. The
*	second field, HelpText, is used by the core CLI to display help information
*	to the user in the core commands `cf help`, `cf`, or `cf -h`.
 */
func (c *FastPushPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "FastPushPlugin",
		Version: plugin.VersionType{
			Major: 1,
			Minor: 0,
			Build: 0,
		},
		MinCliVersion: plugin.VersionType{
			Major: 6,
			Minor: 7,
			Build: 0,
		},
		Commands: []plugin.Command{
			plugin.Command{
				Name:     "fast-push",
				Alias:    "fp",
				HelpText: "fast-push removes the need to deploy your app again for a small change",
				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "fast-push appname\n   cf fp appname",
					Options: map[string]string{
						"dry": "--dry, dry run for fast-push",
					},
				},
			},
		},
	}
}

/*
* Unlike most Go programs, the `Main()` function will not be used to run all of the
* commands provided in your plugin. Main will be used to initialize the plugin
* process, as well as any dependencies you might require for your
* plugin.
 */
func main() {
	plugin.Start(new(FastPushPlugin))
}

func (c *FastPushPlugin) showUsage(args []string) {
	for _, cmd := range c.GetMetadata().Commands {
		if cmd.Name == args[0] {
			fmt.Println("Invalid Usage: \n", cmd.UsageDetails.Usage)
		}
	}
}
<commit_msg>assume the resource will be /fast-push<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/plugin"
)

/*
*	This is the struct implementing the interface defined by the core CLI. It can
*	be found at  "github.com/cloudfoundry/cli/plugin/plugin.go"
*
 */
type FastPushPlugin struct {
	ui terminal.UI
}

/*
*	This function must be implemented by any plugin because it is part of the
*	plugin interface defined by the core CLI.
*
*	Run(....) is the entry point when the core CLI is invoking a command defined
*	by a plugin. The first parameter, plugin.CliConnection, is a struct that can
*	be used to invoke cli commands. The second paramter, args, is a slice of
*	strings. args[0] will be the name of the command, and will be followed by
*	any additional arguments a cli user typed in.
*
*	Any error handling should be handled with the plugin itself (this means printing
*	user facing errors). The CLI will exit 0 if the plugin exits 0 and will exit
*	1 should the plugin exits nonzero.
 */
func (c *FastPushPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	// Ensure that the user called the command fast-push
	// alias fp is auto mapped
	if args[0] != "fast-push" || args[0] != "fp" {
		return
	}

	c.ui = terminal.NewUI(os.Stdin, terminal.NewTeePrinter())

	if cliConnection.IsLoggedIn() == false {
		panic("cannot perform fast-push without being logged in to CF")
	}

	// set flag for dry run
	flagSet := flag.NewFlagSet("fpfs", flag.ExitOnError)
	dryRun := flagSet.Bool("dry", false, "dry run flag")

	err := flagSet.Parse(args[1:])
	if err != nil {
		c.ui.Failed(err.Error())
	}

	if len(args) > 2 {
		fmt.Println("Running the fast-push command")
		fmt.Printf("Target app: %s /n", args[1])
		// check if the user asked for a dry run or not
		if *c.dryRun {
			c.fastPush(cliConnection, args[1], true)
		} else {
			c.fastPush(cliConnection, args[1], false)
		}
	} else {
		c.showUsage(args)
	}

}

func (c *FastPushPlugin) fastPush(cliConnection plugin.CliConnection, appName string, dryRun bool) {
	// Please check what GetApp returns here
	// https://github.com/cloudfoundry/cli/blob/master/plugin/models/get_app.go
	var appUrls []string

	if dryRun {
		c.ui.Warn("warning: No changes will be applied, this is a dry run !!")
	}

	app := c.GetApp(appName)
	routes := app.Routes

	if len(routes) > 1 {
		for _, route := range routes {
			c.ui.Warn("multiple corresponding url's has been found")
			panic("NOT IMPLEMENTED YET!")
		}
	} else {
		c.ui.Say("corresponding app host: %s", routes[0].Host)
		c.ui.Say("corresponding app domain: %s", routes[0].Domain.Name)
	}

	// dispatch request TODO
	url := routes[0].Host
	var query = []byte(`query-here`)
	req, err := http.NewRequest("POST", url+"/fast-push", bytes.NewBuffer(query))
	req.Header.Set("X-Custom-Header", "somevalue")
	req.Header.Set("Content-Type", "text/plain")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	fmt.Println("response Status:", resp.Status)
	fmt.Println("response Headers:", resp.Header)
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))
}

/*
*	This function must be implemented as part of the plugin interface
*	defined by the core CLI.
*
*	GetMetadata() returns a PluginMetadata struct. The first field, Name,
*	determines the name of the plugin which should generally be without spaces.
*	If there are spaces in the name a user will need to properly quote the name
*	during uninstall otherwise the name will be treated as seperate arguments.
*	The second value is a slice of Command structs. Our slice only contains one
*	Command Struct, but could contain any number of them. The first field Name
*	defines the command `cf basic-plugin-command` once installed into the CLI. The
*	second field, HelpText, is used by the core CLI to display help information
*	to the user in the core commands `cf help`, `cf`, or `cf -h`.
 */
func (c *FastPushPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "FastPushPlugin",
		Version: plugin.VersionType{
			Major: 1,
			Minor: 0,
			Build: 0,
		},
		MinCliVersion: plugin.VersionType{
			Major: 6,
			Minor: 7,
			Build: 0,
		},
		Commands: []plugin.Command{
			plugin.Command{
				Name:     "fast-push",
				Alias:    "fp",
				HelpText: "fast-push removes the need to deploy your app again for a small change",
				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "fast-push appname\n   cf fp appname",
					Options: map[string]string{
						"dry": "--dry, dry run for fast-push",
					},
				},
			},
		},
	}
}

/*
* Unlike most Go programs, the `Main()` function will not be used to run all of the
* commands provided in your plugin. Main will be used to initialize the plugin
* process, as well as any dependencies you might require for your
* plugin.
 */
func main() {
	plugin.Start(new(FastPushPlugin))
}

func (c *FastPushPlugin) showUsage(args []string) {
	for _, cmd := range c.GetMetadata().Commands {
		if cmd.Name == args[0] {
			fmt.Println("Invalid Usage: \n", cmd.UsageDetails.Usage)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/spf13/cobra"
	"github.com/tgermain/grandRepositorySky/communicator/receiver"
	"github.com/tgermain/grandRepositorySky/dht"
	"github.com/tgermain/grandRepositorySky/node"
	"github.com/tgermain/grandRepositorySky/shared"
	"runtime"
	"time"
)

func MakeDHTNode(NewId *string, NewIp, NewPort string) *node.DHTnode {
	if NewId == nil || *NewId == "" {

		tempId := dht.GenerateNodeId()
		NewId = &tempId
	}
	shared.Logger.Info("Creating node : \nId %.10v \nIP %s Port %.10s \n", *NewId, NewIp, NewPort)
	//Set the globally shared information
	shared.LocalId = *NewId
	shared.LocalIp = NewIp
	shared.LocalPort = NewPort

	// create node with its commInterface
	newNode, newSenderLink := node.MakeNode()

	receiverLink := receiver.MakeReceiver(newNode, newSenderLink)
	//Make the commInterface listen to incomming messages on globalIp, globalPort
	receiverLink.StartAndListen()

	return newNode
}

func main() {
	shared.SetupLogger()

	var Id string
	var Ip string
	var Port string
	var DistIp string
	var DistPort string
	var join bool

	rootCmd := &cobra.Command{Use: "grandRepositorySky",
		Run: func(cmd *cobra.Command, args []string) {
			node1 := MakeDHTNode(&Id, Ip, Port)
			if join {
				node1.JoinRing(&shared.DistantNode{
					Id:   "A",
					Ip:   DistIp,
					Port: DistPort,
				})
				time.Sleep(time.Second * 5)
				node1.PrintRing()
			}
			for {
				runtime.Gosched()
			}
		},
	}
	rootCmd.Flags().StringVarP(&Id, "Id of the node", "n", "2222", "Id you want for your node")
	rootCmd.Flags().StringVarP(&Ip, "Ip of the node", "i", "localhost", "Ip you want for your node")
	rootCmd.Flags().StringVarP(&Port, "Port of the node", "p", "2222", "port you want for your node")
	rootCmd.Flags().BoolVarP(&join, "joining ?", "j", false, "you wanna join ?")
	rootCmd.Flags().StringVarP(&DistIp, "Ip of the distante node", "w", "localhost", "Ip you want for your node")
	rootCmd.Flags().StringVarP(&DistPort, "Port of the distante node", "d", "4321", "port you want for your node")
	rootCmd.Execute()
}
<commit_msg>hacky things to run node without using 100% CPU whit infinite loop<commit_after>package main

import (
	"github.com/spf13/cobra"
	"github.com/tgermain/grandRepositorySky/communicator/receiver"
	"github.com/tgermain/grandRepositorySky/dht"
	"github.com/tgermain/grandRepositorySky/node"
	"github.com/tgermain/grandRepositorySky/shared"
	"runtime"
	"time"
)

func MakeDHTNode(NewId *string, NewIp, NewPort string) *node.DHTnode {
	if NewId == nil || *NewId == "" {

		tempId := dht.GenerateNodeId()
		NewId = &tempId
	}
	shared.Logger.Info("Creating node : \nId %.10v \nIP %s Port %.10s \n", *NewId, NewIp, NewPort)
	//Set the globally shared information
	shared.LocalId = *NewId
	shared.LocalIp = NewIp
	shared.LocalPort = NewPort

	// create node with its commInterface
	newNode, newSenderLink := node.MakeNode()

	receiverLink := receiver.MakeReceiver(newNode, newSenderLink)
	//Make the commInterface listen to incomming messages on globalIp, globalPort
	receiverLink.StartAndListen()

	return newNode
}

func main() {
	shared.SetupLogger()

	var Id string
	var Ip string
	var Port string
	var DistIp string
	var DistPort string
	var join bool

	rootCmd := &cobra.Command{Use: "grandRepositorySky",
		Run: func(cmd *cobra.Command, args []string) {
			node1 := MakeDHTNode(&Id, Ip, Port)
			if join {
				node1.JoinRing(&shared.DistantNode{
					Id:   "A",
					Ip:   DistIp,
					Port: DistPort,
				})
				time.Sleep(time.Second * 5)
				node1.PrintRing()
			}
			// go func() {
			for {
				time.Sleep(time.Second)
				runtime.Gosched()
			}
			// }()
		},
	}
	rootCmd.Flags().StringVarP(&Id, "Id of the node", "n", "2222", "Id you want for your node")
	rootCmd.Flags().StringVarP(&Ip, "Ip of the node", "i", "localhost", "Ip you want for your node")
	rootCmd.Flags().StringVarP(&Port, "Port of the node", "p", "2222", "port you want for your node")
	rootCmd.Flags().BoolVarP(&join, "joining ?", "j", false, "you wanna join ?")
	rootCmd.Flags().StringVarP(&DistIp, "Ip of the distante node", "w", "localhost", "Ip you want for your node")
	rootCmd.Flags().StringVarP(&DistPort, "Port of the distante node", "d", "4321", "port you want for your node")
	rootCmd.Execute()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"

	"github.com/fsnotify/fsnotify"

	"github.com/NebulousLabs/Sia/api"
)

// SiaFolder is a folder that is synchronized to a Sia node.
type SiaFolder struct {
	client  *api.Client
	watcher *fsnotify.Watcher
}

// NewSiafolder creates a new SiaFolder using the provided path and api
// address.
func NewSiafolder(path string, apiaddr string) (*SiaFolder, error) {
	sf := &SiaFolder{}

	sf.client = api.NewClient(apiaddr, "")
	var contracts api.RenterContracts
	err := sf.client.Get("/renter/contracts", &contracts)
	if err != nil {
		return nil, err
	}
	if len(contracts.Contracts) == 0 {
		return nil, errors.New("you must have formed contracts to upload to Sia")
	}

	// walk the provided path
	var files []string
	err = filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		files = append(files, path)
		return nil
	})
	if err != nil {
		return nil, err
	}

	// upload any files not in the sia node already
	err = sf.uploadNonExisting(files)
	if err != nil {
		return nil, err
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Remove == fsnotify.Remove {
					sf.handleRemove(event.Name)
				}

				if event.Op&fsnotify.Create == fsnotify.Create {
					sf.handleCreate(event.Name)
				}

				if event.Op&fsnotify.Rename == fsnotify.Rename {
					sf.handleRemove(event.Name)
				}
			case err := <-watcher.Errors:
				log.Println("fsevents error:", err)
			}
		}
	}()

	err = watcher.Add(path)
	if err != nil {
		return nil, err
	}

	sf.watcher = watcher
	return sf, nil
}

// Close releases any resources allocated by a SiaFolder.
func (sf *SiaFolder) Close() error {
	return sf.watcher.Close()
}

// handleCreate handles a file creation event. `file` is a relative path to the
// file on disk.
func (sf *SiaFolder) handleCreate(file string) {
	log.Println("sync updated detected, uploading", file)
	abspath, err := filepath.Abs(file)
	if err != nil {
		log.Println("error getting absolute path to upload:", err)
		return
	}
	err = sf.client.Post(fmt.Sprintf("/renter/upload/%v", file), fmt.Sprintf("source=%v", abspath), nil)
	if err != nil {
		log.Printf("error uploading %v: %v\n", file, err)
	}
}

// handleRemove handles a file removal event.
func (sf *SiaFolder) handleRemove(file string) {
	log.Println("sync updated detected, removing", file)
	err := sf.client.Post(fmt.Sprintf("/renter/delete/%v", file), "", nil)
	if err != nil {
		log.Printf("error removing %v: %v\n", file, err)
	}
}

// uploadNonExisting runs once and performs any uploads required to ensure
// every file in files is uploaded to the Sia node.
func (sf *SiaFolder) uploadNonExisting(files []string) error {
	var renterFiles api.RenterFiles
	err := sf.client.Get("/renter/files", &renterFiles)
	if err != nil {
		return err
	}

	for _, diskfile := range files {
		exists := false
		for _, siafile := range renterFiles.Files {
			if siafile.SiaPath == diskfile {
				exists = true
			}
		}

		if !exists {
			sf.handleCreate(diskfile)
		}
	}

	return nil
}

func main() {
	if len(os.Args) == 1 {
		fmt.Println("usage: siasync [folder]")
		os.Exit(1)
	}
	sf, err := NewSiafolder(os.Args[1], "localhost:9980")
	if err != nil {
		log.Fatal(err)
	}
	defer sf.Close()

	done := make(chan os.Signal)
	signal.Notify(done, os.Interrupt)
	<-done
}
<commit_msg>fix upload folder bug, remove rename, use relative path to upload<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"

	"github.com/fsnotify/fsnotify"

	"github.com/NebulousLabs/Sia/api"
)

// SiaFolder is a folder that is synchronized to a Sia node.
type SiaFolder struct {
	path    string
	client  *api.Client
	watcher *fsnotify.Watcher
}

// NewSiafolder creates a new SiaFolder using the provided path and api
// address.
func NewSiafolder(path string, apiaddr string) (*SiaFolder, error) {
	sf := &SiaFolder{}
	sf.path = path

	sf.client = api.NewClient(apiaddr, "")
	var contracts api.RenterContracts
	err := sf.client.Get("/renter/contracts", &contracts)
	if err != nil {
		return nil, err
	}
	if len(contracts.Contracts) == 0 {
		return nil, errors.New("you must have formed contracts to upload to Sia")
	}

	// walk the provided path
	var files []string
	err = filepath.Walk(path, func(filepath string, f os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if filepath == path {
			return nil
		}

		files = append(files, filepath)
		return nil
	})
	if err != nil {
		return nil, err
	}

	// upload any files not in the sia node already
	err = sf.uploadNonExisting(files)
	if err != nil {
		return nil, err
	}

	// watch for file changes
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Remove == fsnotify.Remove {
					sf.handleRemove(event.Name)
				}

				if event.Op&fsnotify.Create == fsnotify.Create {
					sf.handleCreate(event.Name)
				}

			case err := <-watcher.Errors:
				log.Println("fsevents error:", err)
			}
		}
	}()
	err = watcher.Add(path)
	if err != nil {
		return nil, err
	}

	sf.watcher = watcher
	return sf, nil
}

// Close releases any resources allocated by a SiaFolder.
func (sf *SiaFolder) Close() error {
	return sf.watcher.Close()
}

// handleCreate handles a file creation event. `file` is a relative path to the
// file on disk.
func (sf *SiaFolder) handleCreate(file string) {
	log.Println("sync updated detected, uploading", file)
	abspath, err := filepath.Abs(file)
	if err != nil {
		log.Println("error getting absolute path to upload:", err)
		return
	}
	relpath, err := filepath.Rel(sf.path, file)
	if err != nil {
		log.Println("error getting relative path to upload:", err)
		return
	}
	err = sf.client.Post(fmt.Sprintf("/renter/upload/%v", relpath), fmt.Sprintf("source=%v", abspath), nil)
	if err != nil {
		log.Printf("error uploading %v: %v\n", file, err)
	}
}

// handleRemove handles a file removal event.
func (sf *SiaFolder) handleRemove(file string) {
	log.Println("sync updated detected, removing", file)
	relpath, err := filepath.Rel(sf.path, file)
	if err != nil {
		log.Println("error getting relative path to remove:", err)
		return
	}
	err = sf.client.Post(fmt.Sprintf("/renter/delete/%v", relpath), "", nil)
	if err != nil {
		log.Printf("error removing %v: %v\n", file, err)
	}
}

// uploadNonExisting runs once and performs any uploads required to ensure
// every file in files is uploaded to the Sia node.
func (sf *SiaFolder) uploadNonExisting(files []string) error {
	var renterFiles api.RenterFiles
	err := sf.client.Get("/renter/files", &renterFiles)
	if err != nil {
		return err
	}

	for _, diskfile := range files {
		exists := false
		for _, siafile := range renterFiles.Files {
			if siafile.SiaPath == diskfile {
				exists = true
			}
		}

		if !exists {
			sf.handleCreate(diskfile)
		}
	}

	return nil
}

func main() {
	if len(os.Args) == 1 {
		fmt.Println("usage: siasync [folder]")
		os.Exit(1)
	}
	sf, err := NewSiafolder(os.Args[1], "localhost:9980")
	if err != nil {
		log.Fatal(err)
	}
	defer sf.Close()

	done := make(chan os.Signal)
	signal.Notify(done, os.Interrupt)
	<-done
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/spf13/cobra"
)

// go run *.go archive --artifact=main.go --group=com.ubanita --version=1.0-SNAPSHOT
// go run *.go fetch --artifact=main.go --group=com.ubanita --version=1.0-SNAPSHOT hier

var VERSION string = "dev"
var BUILDDATE string = "now"

var RootCmd = &cobra.Command{
	Use:   "typhoon",
	Short: "typhoon a is a tool for artifact management",
	Run:   func(cmd *cobra.Command, args []string) {},
}

type artifactCmd struct {
	*cobra.Command
	artifact string
	group    string
	version  string
}

type archiveCmd struct {
	*artifactCmd
	overwrite bool
}

func newArtifactCmd(cobraCmd *cobra.Command) *artifactCmd {
	cmd := new(artifactCmd)
	cmd.Command = cobraCmd
	cmd.PersistentFlags().StringVar(&cmd.artifact, "artifact", ".", "file location of artifact to copy")
	cmd.PersistentFlags().StringVar(&cmd.group, "group", ".", "store the articat under this group")
	cmd.PersistentFlags().StringVar(&cmd.version, "version", ".", "store the articat under this version")
	return cmd
}

func newArchiveCmd() *cobra.Command {
	cmd := newArtifactCmd(&cobra.Command{
		Use:   "archive [artifact]",
		Short: "copy an artifact to the typhoon repository",
	})
	archiveCmd := new(archiveCmd)
	archiveCmd.artifactCmd = cmd
	archiveCmd.PersistentFlags().BoolVar(&archiveCmd.overwrite, "force", false, "force overwrite if version exists")
	cmd.Command.Run = archiveCmd.doArchive
	return cmd.Command
}

func getRepo() string {
	repo := os.Getenv("TYPHOON_REPO")
	if len(repo) == 0 {
		log.Fatal("missing TYPHOON_REPO environment setting")
	}
	return repo
}

func (a *archiveCmd) doArchive(cmd *cobra.Command, args []string) {
	if len(args) == 0 {
		log.Fatalf("missing artifact")
	}
	a.artifact = args[len(args)-1]
	g := path.Join(strings.Split(a.group, ".")...)
	regular := path.Base(path.Clean(a.artifact))
	p := path.Join(getRepo(), g, regular, a.version)

	log.Printf("copying %s into folder %s\n", regular, p)
	if err := os.MkdirAll(p, os.ModePerm); err != nil {
		log.Fatalf("unable to create dirs: %s cause: %v", p, err)
	}
	dest := path.Join(p, regular)

	// SNAPSHOT can be overwritten
	if strings.HasSuffix(a.version, "SNAPSHOT") {
		log.Println("will overwrite|create SNAPSHOT version")
		a.overwrite = true
	}
	if !a.overwrite && Exists(dest) {
		log.Fatalf("unable to copy artifact: %s to: %s cause: it already exists and --force=false", regular, p)
	}
	if err := Cp(dest, a.artifact); err != nil {
		log.Fatalf("unable to copy artifact: %s to: %s cause:%v", regular, p, err)
	}
}

type fetchCmd struct {
	*artifactCmd
	destination string
}

func newFetchCmd() *cobra.Command {
	cmd := newArtifactCmd(&cobra.Command{
		Use:   "fetch [destination]",
		Short: "copy an artifact from the typhoon repository to [destination]",
	})
	fetch := new(fetchCmd)
	fetch.artifactCmd = cmd
	cmd.Command.Run = fetch.doFetch
	return cmd.Command
}

func (f *fetchCmd) doFetch(cmd *cobra.Command, args []string) {
	g := path.Join(strings.Split(f.group, ".")...)
	src := path.Join(getRepo(), g, f.artifact, f.version, f.artifact)
	if len(args) == 0 {
		log.Fatalf("missing destination")
	}
	destination := args[len(args)-1]
	log.Printf("copying %s to %s\n", src, destination)
	if err := Cp(destination, src); err != nil {
		log.Fatalf("unable to copy artifact: %s to: %s cause:%v", src, f.destination, err)
	}
}

func main() {
	log.Println("_/^\\_")
	log.Println(" | | typhoon - the artifact tool [commit=", VERSION, "build=", BUILDDATE, "]")
	log.Println("-\\_/-")
	RootCmd.AddCommand(newArchiveCmd())
	RootCmd.AddCommand(newFetchCmd())
	RootCmd.Execute()
}

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

func Cp(dst, src string) error {
	return exec.Command("cp", src, dst).Run()
}

// Copy does what is says. Ignores errors on Close though.
func Copy(dst, src string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()
	_, err = io.Copy(out, in)
	cerr := out.Close()
	if err != nil {
		return err
	}
	return cerr
}
<commit_msg>add list command<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/spf13/cobra"
)

// go run *.go archive --artifact=main.go --group=com.ubanita --version=1.0-SNAPSHOT
// go run *.go fetch --artifact=main.go --group=com.ubanita --version=1.0-SNAPSHOT hier
// go run *.go list --group=com.ubanita --artifact=lucifer --version=1.0-SNAPSHOT

var VERSION string = "dev"
var BUILDDATE string = "now"

func main() {
	log.Println("_/^\\_")
	log.Println(" | | typhoon - the artifact tool [commit=", VERSION, "build=", BUILDDATE, "]")
	log.Println("-\\_/-")
	RootCmd.AddCommand(newArchiveCmd())
	RootCmd.AddCommand(newFetchCmd())
	RootCmd.AddCommand(newListCmd())
	RootCmd.Execute()
}

var RootCmd = &cobra.Command{
	Use:   "typhoon",
	Short: "typhoon a is a tool for artifact management",
	Run:   func(cmd *cobra.Command, args []string) {},
}

type artifactCmd struct {
	*cobra.Command
	artifact string
	group    string
	version  string
}

func newListCmd() *cobra.Command {
	cmd := newArtifactCmd(&cobra.Command{
		Use:   "list",
		Short: "list all available artifacts from the typhoon repository",
	})
	cmd.Command.Run = cmd.doList
	return cmd.Command
}

func (c *artifactCmd) doList(cmd *cobra.Command, args []string) {
	g := path.Join(strings.Split(c.group, ".")...)
	group := path.Join(getRepo(), g, c.artifact, c.version)
	files, _ := ioutil.ReadDir(group)
	for _, f := range files {
		fmt.Println(f.Name())
	}
}

type archiveCmd struct {
	*artifactCmd
	overwrite bool
}

func newArtifactCmd(cobraCmd *cobra.Command) *artifactCmd {
	cmd := new(artifactCmd)
	cmd.Command = cobraCmd
	cmd.PersistentFlags().StringVar(&cmd.artifact, "artifact", ".", "file location of artifact to copy")
	cmd.PersistentFlags().StringVar(&cmd.group, "group", ".", "folder containing the artifacts")
	cmd.PersistentFlags().StringVar(&cmd.version, "version", ".", "version of the artifact")
	return cmd
}

func newArchiveCmd() *cobra.Command {
	cmd := newArtifactCmd(&cobra.Command{
		Use:   "archive [artifact]",
		Short: "copy an artifact to the typhoon repository",
	})
	archiveCmd := new(archiveCmd)
	archiveCmd.artifactCmd = cmd
	archiveCmd.PersistentFlags().BoolVar(&archiveCmd.overwrite, "force", false, "force overwrite if version exists")
	cmd.Command.Run = archiveCmd.doArchive
	return cmd.Command
}

func getRepo() string {
	repo := os.Getenv("TYPHOON_REPO")
	if len(repo) == 0 {
		log.Fatal("missing TYPHOON_REPO environment setting")
	}
	return repo
}

func (a *archiveCmd) doArchive(cmd *cobra.Command, args []string) {
	if len(args) == 0 {
		log.Fatalf("missing artifact")
	}
	a.artifact = args[len(args)-1]
	g := path.Join(strings.Split(a.group, ".")...)
	regular := path.Base(path.Clean(a.artifact))
	p := path.Join(getRepo(), g, regular, a.version)

	log.Printf("copying %s into folder %s\n", regular, p)
	if err := os.MkdirAll(p, os.ModePerm); err != nil {
		log.Fatalf("unable to create dirs: %s cause: %v", p, err)
	}
	dest := path.Join(p, regular)

	// SNAPSHOT can be overwritten
	if strings.HasSuffix(a.version, "SNAPSHOT") {
		log.Println("will overwrite|create SNAPSHOT version")
		a.overwrite = true
	}
	if !a.overwrite && Exists(dest) {
		log.Fatalf("unable to copy artifact: %s to: %s cause: it already exists and --force=false", regular, p)
	}
	if err := Cp(dest, a.artifact); err != nil {
		log.Fatalf("unable to copy artifact: %s to: %s cause:%v", regular, p, err)
	}
}

type fetchCmd struct {
	*artifactCmd
}

func newFetchCmd() *cobra.Command {
	cmd := newArtifactCmd(&cobra.Command{
		Use:   "fetch [destination]",
		Short: "copy an artifact from the typhoon repository to [destination]",
	})
	fetch := new(fetchCmd)
	fetch.artifactCmd = cmd
	cmd.Command.Run = fetch.doFetch
	return cmd.Command
}

func (f *fetchCmd) doFetch(cmd *cobra.Command, args []string) {
	g := path.Join(strings.Split(f.group, ".")...)
	src := path.Join(getRepo(), g, f.artifact, f.version, f.artifact)
	if len(args) == 0 {
		log.Fatalf("missing destination")
	}
	if !Exists(src) {
		log.Fatalf("unable to copy artifact: %s because: no such artifact", src)
	}
	destination := args[len(args)-1]
	log.Printf("copying %s to %s\n", src, destination)
	if err := Cp(destination, src); err != nil {
		log.Fatalf("unable to copy artifact: %s to: %s because:%v", src, destination, err)
	}
}

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

func Cp(dst, src string) error {
	return exec.Command("cp", src, dst).Run()
}

// Copy does what is says. Ignores errors on Close though.
func Copy(dst, src string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()
	_, err = io.Copy(out, in)
	cerr := out.Close()
	if err != nil {
		return err
	}
	return cerr
}
<|endoftext|>


<commit_before>package main

import (
    "flag"
    "fmt"
    "os"
    "strings"

    "github.com/op/go-logging"

    "github.com/BattleRattle/sexy/sentry"
    "github.com/BattleRattle/sexy/version"
    "github.com/BattleRattle/sexy/udp"
    "github.com/BurntSushi/toml"
)

type Config struct {
    UdpAddress  string
    SentryUrl   string
    Concurrency uint
    Buffer      uint
    LogLevel    string
    LogFile     string
}

var (
    configFile = flag.String("c", "/etc/sexy/sexy.toml", "Path to config file")
    showVersion = flag.Bool("version", false, "Show version information")

    format = logging.MustStringFormatter(`%{time:15:04:05.000} %{level:.4s} %{message}`)
    logger = logging.MustGetLogger("sexy")
)

func main() {
    flag.Parse()

    if *showVersion {
        printVersion()
        return
    }

    var cfg Config
    if _, err := toml.DecodeFile(*configFile, &cfg); err != nil {
        fmt.Fprintln(os.Stderr, "Failed to load config file: ", err)
        os.Exit(1)
    }

    logFile, err := os.OpenFile(cfg.LogFile, os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0660)
    if err != nil {
        fmt.Fprintln(os.Stderr, "Failed to open log file " + cfg.LogFile, err)
        os.Exit(1)
    }

    lvl, err := logging.LogLevel(strings.ToUpper(cfg.LogLevel))
    if err != nil {
        fmt.Fprintln(os.Stderr, "Invalid log level. Allowed levels are: DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL")
        os.Exit(1)
    }

    logging.SetBackend(logging.NewBackendFormatter(logging.NewLogBackend(logFile, "", 0), format))
    logging.SetLevel(lvl, "sexy")

    fmt.Println(fmt.Sprintf("SEXY - Sentry Proxy %s (%s)", version.Version, version.CommitHash))

    if cfg.UdpAddress == "" {
        fmt.Fprintln(os.Stderr, "No UDP address given to listen to\nRun `sexy -help` to display available arguments")
        os.Exit(1)
    }

    if cfg.SentryUrl == "" {
        fmt.Fprintln(os.Stderr, "No Sentry URL given\nRun `sexy -help` to display available arguments")
        os.Exit(1)
    }

    if cfg.Concurrency < 1 {
        fmt.Fprintln(os.Stderr, "Concurrency level must be 1 or higher")
        os.Exit(1)
    }

    if cfg.Buffer < 1 {
        fmt.Fprintln(os.Stderr, "Buffer size must be 1 or higher")
        os.Exit(1)
    }

    chMsg := make(chan sentry.Message, cfg.Buffer)
    defer close(chMsg)

    for i := uint(0); i < cfg.Concurrency; i++ {
        go sentry.NewWorker(cfg.SentryUrl, chMsg, logger).Run()
    }

    udp.NewServer(cfg.UdpAddress, chMsg, logger).Run()
}

func printVersion() {
    fmt.Println("SEXY - Sentry Proxy")
    fmt.Println()
    fmt.Println("Version:         ", version.Version)
    fmt.Println("Git Commit Hash: ", version.CommitHash)
    fmt.Println("Build Time:      ", version.BuildTime)
}<commit_msg>show the date in log<commit_after>package main

import (
    "flag"
    "fmt"
    "os"
    "strings"

    "github.com/BurntSushi/toml"
    "github.com/op/go-logging"

    "github.com/BattleRattle/sexy/sentry"
    "github.com/BattleRattle/sexy/udp"
    "github.com/BattleRattle/sexy/version"
)

type Config struct {
    UdpAddress  string
    SentryUrl   string
    Concurrency uint
    Buffer      uint
    LogLevel    string
    LogFile     string
}

var (
    configFile = flag.String("c", "/etc/sexy/sexy.toml", "Path to config file")
    showVersion = flag.Bool("version", false, "Show version information")

    format = logging.MustStringFormatter(`%{time:2006-01-02 15:04:05.000} %{level:.4s} %{message}`)
    logger = logging.MustGetLogger("sexy")
)

func main() {
    flag.Parse()

    if *showVersion {
        printVersion()
        return
    }

    var cfg Config
    if _, err := toml.DecodeFile(*configFile, &cfg); err != nil {
        fmt.Fprintln(os.Stderr, "Failed to load config file: ", err)
        os.Exit(1)
    }

    logFile, err := os.OpenFile(cfg.LogFile, os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0660)
    if err != nil {
        fmt.Fprintln(os.Stderr, "Failed to open log file " + cfg.LogFile, err)
        os.Exit(1)
    }

    lvl, err := logging.LogLevel(strings.ToUpper(cfg.LogLevel))
    if err != nil {
        fmt.Fprintln(os.Stderr, "Invalid log level. Allowed levels are: DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL")
        os.Exit(1)
    }

    logging.SetBackend(logging.NewBackendFormatter(logging.NewLogBackend(logFile, "", 0), format))
    logging.SetLevel(lvl, "sexy")

    fmt.Println(fmt.Sprintf("SEXY - Sentry Proxy %s (%s)", version.Version, version.CommitHash))

    if cfg.UdpAddress == "" {
        fmt.Fprintln(os.Stderr, "No UDP address given to listen to\nRun `sexy -help` to display available arguments")
        os.Exit(1)
    }

    if cfg.SentryUrl == "" {
        fmt.Fprintln(os.Stderr, "No Sentry URL given\nRun `sexy -help` to display available arguments")
        os.Exit(1)
    }

    if cfg.Concurrency < 1 {
        fmt.Fprintln(os.Stderr, "Concurrency level must be 1 or higher")
        os.Exit(1)
    }

    if cfg.Buffer < 1 {
        fmt.Fprintln(os.Stderr, "Buffer size must be 1 or higher")
        os.Exit(1)
    }

    chMsg := make(chan sentry.Message, cfg.Buffer)
    defer close(chMsg)

    for i := uint(0); i < cfg.Concurrency; i++ {
        go sentry.NewWorker(cfg.SentryUrl, chMsg, logger).Run()
    }

    udp.NewServer(cfg.UdpAddress, chMsg, logger).Run()
}

func printVersion() {
    fmt.Println("SEXY - Sentry Proxy")
    fmt.Println()
    fmt.Println("Version:         ", version.Version)
    fmt.Println("Git Commit Hash: ", version.CommitHash)
    fmt.Println("Build Time:      ", version.BuildTime)
}<|endoftext|>