diff options
| author | terminaldweller <thabogre@gmail.com> | 2021-03-13 11:34:40 +0000 | 
|---|---|---|
| committer | terminaldweller <thabogre@gmail.com> | 2021-03-13 11:34:40 +0000 | 
| commit | b947ffe4785c2a6a28965d34b926e163ff220efa (patch) | |
| tree | 84b38fcaa6493b2149cc0bafd484cf963e9ba470 | |
| parent | added the goreportcard badge. (diff) | |
| download | hived-b947ffe4785c2a6a28965d34b926e163ff220efa.tar.gz hived-b947ffe4785c2a6a28965d34b926e163ff220efa.zip | |
added graceful shutdown.fixed the -help crashing problem. wasnt setting the content type. added CRUD for the alert endpoint. now using gorilla mux for routing.
Diffstat (limited to '')
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | go.mod | 1 | ||||
| -rw-r--r-- | go.sum | 2 | ||||
| -rw-r--r-- | hived.go | 151 | 
4 files changed, 133 insertions, 26 deletions
| @@ -62,5 +62,6 @@ You can find the swagger and postman docs under `/api`.<br/>  ## TODO  * ~~fix travis~~  * add unit tests -* fix `hived -help` crashing -* haproxy-ssl-termination +* ~~fix `hived -help` crashing~~ +* haproxy +* turn the telegram bot into its own microservice @@ -6,6 +6,7 @@ require (  	github.com/Knetic/govaluate v3.0.0+incompatible  	github.com/go-redis/redis/v8 v8.6.0  	github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible +	github.com/gorilla/mux v1.8.0  	github.com/rs/zerolog v1.20.0  	github.com/technoweenie/multipartstreamer v1.0.1 // indirect  ) @@ -26,6 +26,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw  github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=  github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=  github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=  github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=  github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=  github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -14,6 +14,7 @@ import (  	"net/http"  	"net/url"  	"os" +	"os/signal"  	"strconv"  	"sync"  	"time" @@ -21,6 +22,7 @@ import (  	"github.com/Knetic/govaluate"  	"github.com/go-redis/redis/v8"  	"github.com/go-telegram-bot-api/telegram-bot-api" +	"github.com/gorilla/mux"  	"github.com/rs/zerolog"  	"github.com/rs/zerolog/log"  ) @@ -92,12 +94,11 @@ func sendGetToCryptoCompare(  	params := "fsym=" + url.QueryEscape(name) + "&" +  		"tsyms=" + url.QueryEscape(unit)  	path := cryptocomparePriceURL + params -	fmt.Println(path)  	resp, err := http.Get(path)  	if err != nil {  		priceChan <- priceChanStruct{name: name, price: 0.}  		errChan <- errorChanStruct{hasError: true, err: err} -		fmt.Println(err.Error()) +		log.Error().Err(err)  	}  	defer resp.Body.Close() @@ -105,7 +106,7 @@ func sendGetToCryptoCompare(  	if err != nil {  		priceChan <- priceChanStruct{name: name, price: 0.}  		errChan <- errorChanStruct{hasError: true, err: err} -		fmt.Println(err.Error()) +		log.Error().Err(err)  	}  	jsonBody := make(map[string]float64) @@ -113,16 +114,17 @@ func sendGetToCryptoCompare(  	if err != nil {  		priceChan <- priceChanStruct{name: name, price: 0.}  		errChan <- errorChanStruct{hasError: true, err: err} -		fmt.Println(err.Error()) +		log.Error().Err(err)  	} -	fmt.Println(string(body)) +	log.Info().Msg(string(body))  	priceChan <- priceChanStruct{name: name, price: jsonBody[unit]}  	errChan <- errorChanStruct{hasError: false, err: nil}  }  func priceHandler(w http.ResponseWriter, r *http.Request) { +	w.Header().Add("Content-Type", "application/json")  	if r.Method != "GET" {  		http.Error(w, "Method is not supported.", http.StatusNotFound)  	} @@ -185,6 +187,7 @@ func priceHandler(w http.ResponseWriter, r *http.Request) {  func pairHandler(w http.ResponseWriter, r *http.Request) {  	var err error +	w.Header().Add("Content-Type", "application/json")  	if r.Method != "GET" {  		http.Error(w, "Method is not supported.", http.StatusNotFound)  	} @@ -252,7 +255,7 @@ func pairHandler(w http.ResponseWriter, r *http.Request) {  	}  	ratio := priceOne * multiplier / priceTwo -	fmt.Println(ratio) +	log.Info().Msg(fmt.Sprintf("%v", ratio))  	json.NewEncoder(w).Encode(map[string]interface{}{"ratio": ratio})  } @@ -291,7 +294,7 @@ func alertManager() {  			log.Error().Err(err)  			return  		} -		fmt.Println(alerts) +		log.Info().Msg(fmt.Sprintf("%v", alerts))  		for i := range alerts.Alerts {  			expression, err := govaluate.NewEvaluableExpression(alerts.Alerts[i].Expr) @@ -335,14 +338,14 @@ func alertManager() {  				}  			} -			fmt.Println("parameters:", parameters) +			log.Info().Msg(fmt.Sprintf("parameters: %v", parameters))  			result, err := expression.Evaluate(parameters)  			if err != nil {  				log.Error().Err(err)  			}  			var resultBool bool -			fmt.Println("result:", result) +			log.Info().Msg(fmt.Sprintf("result: %v", result))  			resultBool = result.(bool)  			if resultBool == true {  				// bot := getTgBot() @@ -367,6 +370,7 @@ type addAlertJSONType struct {  }  func handleAlertPost(w http.ResponseWriter, r *http.Request) { +	w.Header().Add("Content-Type", "application/json")  	bodyBytes, err := ioutil.ReadAll(r.Body)  	if err != nil {  		log.Printf(err.Error()) @@ -395,10 +399,11 @@ func handleAlertPost(w http.ResponseWriter, r *http.Request) {  func handleAlertDelete(w http.ResponseWriter, r *http.Request) {  	var Id string +	w.Header().Add("Content-Type", "application/json")  	params := r.URL.Query()  	for key, value := range params {  		switch key { -		case "id": +		case "key":  			Id = value[0]  		default:  			log.Error().Err(errors.New("bad parameters for the crypto endpoint.")) @@ -426,11 +431,59 @@ func handleAlertDelete(w http.ResponseWriter, r *http.Request) {  	}{IsSuccessful: true, Err: ""})  } +func handleAlertGet(w http.ResponseWriter, r *http.Request) { +	var Id string +	w.Header().Add("Content-Type", "application/json") +	params := r.URL.Query() +	for key, value := range params { +		switch key { +		case "key": +			Id = value[0] +		default: +			log.Error().Err(errors.New("bad parameters for the crypto endpoint.")) +		} +	} + +	if Id == "" { +		json.NewEncoder(w).Encode(map[string]interface{}{ +			"isSuccessful": false, +			"error":        "Id parameter is not valid."}) +		log.Fatal().Err(errors.New("not all parameters are valid.")) +		return +	} + +	ctx := context.Background() + +	redisResult := rdb.Get(ctx, Id) +	redisResultString, err := redisResult.Result() +	if err != nil { +		log.Err(err) +	} + +	var ErrorString string +	if err == nil { +		ErrorString = "" +	} else { +		ErrorString = err.Error() +	} + +	w.Header().Add("Content-Type", "application/json") + +	json.NewEncoder(w).Encode(struct { +		IsSuccessful bool   `json:"isSuccessful"` +		Error        string `json:"error"` +		Key          string `json:"key"` +		Expr         string `json:"expr"` +	}{IsSuccessful: true, Error: ErrorString, Key: Id, Expr: redisResultString}) +} +  func alertHandler(w http.ResponseWriter, r *http.Request) { -	if r.Method == "POST" { +	if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {  		handleAlertPost(w, r)  	} else if r.Method == "DELETE" {  		handleAlertDelete(w, r) +	} else if r.Method == "GET" { +		handleAlertGet(w, r)  	} else {  		http.Error(w, "Method is not supported.", http.StatusNotFound)  	} @@ -438,6 +491,7 @@ func alertHandler(w http.ResponseWriter, r *http.Request) {  }  func exHandler(w http.ResponseWriter, r *http.Request) { +	w.Header().Add("Content-Type", "application/json")  	if r.Method != "GET" {  		http.Error(w, "Method is not supported.", http.StatusNotFound)  	} @@ -500,26 +554,71 @@ func exHandler(w http.ResponseWriter, r *http.Request) {  }  func healthHandler(w http.ResponseWriter, r *http.Request) { +	var RedisError string +	var HivedError string +	IsHivedOk := true +	var IsRedisOk bool + +	w.Header().Add("Content-Type", "application/json")  	if r.Method != "GET" {  		http.Error(w, "Method is not supported.", http.StatusNotFound)  	} +	pingCtx := context.Background() +	pingResponse := rdb.Ping(pingCtx) +	pingResponseResult, err := pingResponse.Result() +	if err != nil { +		log.Err(err) +		IsRedisOk = false +		RedisError = err.Error() +	} else { +		if pingResponseResult == "PONG" { +			IsRedisOk = true +			RedisError = "" +		} else { +			IsRedisOk = false +			RedisError = "redis did not respond PONG to ping" +		} +	} + +	w.WriteHeader(http.StatusOK) +  	json.NewEncoder(w).Encode(struct { -		IsOK bool   `json:"isOK"` -		Err  string `json:"err"` -	}{IsOK: true, Err: ""}) +		IsHivedOk  bool   `json:"isHivedOk"` +		HivedError string `json:"hivedError"` +		IsRedisOk  bool   `json:"isRedisOk"` +		RedisError string `json:"redisError"` +	}{IsHivedOk: IsHivedOk, HivedError: HivedError, IsRedisOk: IsRedisOk, RedisError: RedisError})  } -func startServer() { -	http.HandleFunc("/health", healthHandler) -	http.HandleFunc("/price", priceHandler) -	http.HandleFunc("/pair", pairHandler) -	http.HandleFunc("/alert", alertHandler) -	http.HandleFunc("/ex", exHandler) - -	if err := http.ListenAndServe(":"+*flagPort, nil); err != nil { -		log.Fatal().Err(err) +func startServer(gracefulWait time.Duration) { +	r := mux.NewRouter() +	srv := &http.Server{ +		Addr:         "0.0.0.0:" + *flagPort, +		WriteTimeout: time.Second * 15, +		ReadTimeout:  time.Second * 15, +		Handler:      r,  	} +	r.HandleFunc("/health", healthHandler) +	r.HandleFunc("/price", priceHandler) +	r.HandleFunc("/pair", pairHandler) +	r.HandleFunc("/alert", alertHandler) +	r.HandleFunc("/ex", exHandler) + +	go func() { +		if err := srv.ListenAndServe(); err != nil { +			log.Fatal().Err(err) +		} +	}() + +	c := make(chan os.Signal, 1) + +	signal.Notify(c, os.Interrupt) +	<-c +	ctx, cancel := context.WithTimeout(context.Background(), gracefulWait) +	defer cancel() +	srv.Shutdown(ctx) +	log.Info().Msg("gracefully shut down the server")  }  func setupLogging() { @@ -527,6 +626,10 @@ func setupLogging() {  }  func main() { +	var gracefulWait time.Duration +	flag.DurationVar(&gracefulWait, "gracefulwait", time.Second*15, "the duration to wait during the graceful shutdown") +	flag.Parse() +  	rdb = redis.NewClient(&redis.Options{  		Addr:     *redisAddress,  		Password: *redisPassword, @@ -537,5 +640,5 @@ func main() {  	setupLogging()  	// go runTgBot()  	go alertManager() -	startServer() +	startServer(gracefulWait)  } | 
