aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKumar Damani <damani.kumar@gmail.com>2020-06-10 10:53:46 +0000
committerKumar Damani <damani.kumar@gmail.com>2020-06-10 10:53:46 +0000
commit16a60795670e48b06e36f73a5a890edbb4491d19 (patch)
treea34a0a107271c548020613197c2e426da142a127 /src
initial commitHEADmaster
Diffstat (limited to 'src')
-rw-r--r--src/app.go50
-rw-r--r--src/common/common.go32
-rw-r--r--src/jobCheckin/jobCheckin.go114
3 files changed, 196 insertions, 0 deletions
diff --git a/src/app.go b/src/app.go
new file mode 100644
index 0000000..0581bfb
--- /dev/null
+++ b/src/app.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+ "app/common"
+ "app/jobCheckin"
+ "log"
+ "net/http"
+ "os"
+)
+
+func pingHandler(w http.ResponseWriter, r *http.Request) {
+ common.JsonRespond(&w, http.StatusOK, "pong")
+ return
+}
+
+func main() {
+ // define urls being served
+ pingEndpoint := "ping"
+ checkinEndpoint := "check-in"
+
+ // create mux
+ mux := http.NewServeMux()
+
+ // define handlers for endpoints here
+ mux.HandleFunc("/"+pingEndpoint, pingHandler)
+ mux.HandleFunc("/"+pingEndpoint+"/", pingHandler)
+
+ // check-in handled by jobCheckin package
+ jobCheckinHandler := jobCheckin.MakeHTTPHandler(checkinEndpoint)
+ mux.Handle("/"+checkinEndpoint, jobCheckinHandler)
+ mux.Handle("/"+checkinEndpoint+"/", jobCheckinHandler)
+
+ // add more routes... eg. /foo handled by foo package
+ // ...
+
+ log.Println("Starting Restful services...")
+ log.Println("Using port:", os.Getenv("HTTP_LISTEN_PORT"))
+
+ // start listening
+ err := http.ListenAndServe(":"+os.Getenv("HTTP_LISTEN_PORT"), mux)
+ log.Print(err)
+ errorHandler(err)
+}
+
+func errorHandler(err error) {
+ if err != nil {
+ log.Println(err)
+ os.Exit(1)
+ }
+}
diff --git a/src/common/common.go b/src/common/common.go
new file mode 100644
index 0000000..3e50632
--- /dev/null
+++ b/src/common/common.go
@@ -0,0 +1,32 @@
+// ONLY PUT FUNCTIONS / VARS THAT WILL BE USED BY MANY PACKAGES
+package common
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/gorilla/mux"
+ "log"
+ "net/http"
+)
+
+// Every respose MUST be a single string.
+type response struct {
+ Mssg string
+}
+
+// Builds and write json response given http status code, and message.
+func JsonRespond(w *http.ResponseWriter, rc int, body string) {
+ res1D := &response{Mssg: body}
+ res1B, _ := json.Marshal(res1D)
+ (*w).WriteHeader(rc)
+ log.Printf("RC: %d | Message: %s", rc, body)
+ fmt.Fprintln(*w, string(res1B))
+}
+
+// Assign a handler function to an endpoint.
+func MakeHTTPHandler(route string, h http.HandlerFunc) http.Handler {
+ r := mux.NewRouter()
+ r.HandleFunc("/"+route, h)
+ r.HandleFunc("/"+route+"/", h)
+ return r
+}
diff --git a/src/jobCheckin/jobCheckin.go b/src/jobCheckin/jobCheckin.go
new file mode 100644
index 0000000..0a1dd1b
--- /dev/null
+++ b/src/jobCheckin/jobCheckin.go
@@ -0,0 +1,114 @@
+package jobCheckin
+
+import (
+ "app/common"
+ "encoding/json"
+ client "github.com/influxdata/influxdb1-client/v2"
+ "log"
+ "net/http"
+ "os"
+ "regexp"
+ "strings"
+)
+
+type Job struct {
+ Source string
+}
+
+// Validates the incomming request and writes an appropriate response.
+// 405: when not POST request
+// 401: when body is not given, or 'source' field missing
+// 500: failed to complete
+// 202: successfully complete
+func jobCheckinHandler(w http.ResponseWriter, r *http.Request) {
+ log.Println("New request ===== ")
+
+ if r.Method != "POST" {
+ common.JsonRespond(&w, http.StatusMethodNotAllowed, "This end-point only accepts POST requests.")
+ return
+ }
+
+ var j Job
+ err := json.NewDecoder(r.Body).Decode(&j)
+ if err != nil {
+ common.JsonRespond(&w, http.StatusBadRequest, err.Error())
+ return
+ }
+ if j.Source == "" {
+ common.JsonRespond(&w, http.StatusBadRequest, "no 'source' field in body. Try sending a json such as {'source': 'job_name'} in the body.")
+ return
+ }
+ log.Println("source:", j.Source)
+
+ measurement, err := chekinJob(j.Source)
+ if err != nil {
+ common.JsonRespond(&w, http.StatusInternalServerError, "Error while writing to Influx.")
+ return
+ }
+
+ common.JsonRespond(&w, http.StatusAccepted, measurement)
+ return
+}
+
+// Write job check-in to influx.
+func chekinJob(jobName string) (string, error) {
+ jobNameClean, err := sanitizeJobName(jobName)
+ if err != nil {
+ return "", err
+ }
+
+ // Make client
+ c, err := client.NewHTTPClient(client.HTTPConfig{
+ Addr: "http://" + os.Getenv("INFLUX_HOST") + ":" + os.Getenv("INFLUX_PORT"),
+ })
+ if err != nil {
+ return "", err
+ }
+ defer c.Close()
+
+ // Create a new point batch
+ bp, _ := client.NewBatchPoints(client.BatchPointsConfig{
+ Database: os.Getenv("INFLUX_DB"),
+ Precision: "s",
+ RetentionPolicy: os.Getenv("INFLUX_RP"),
+ })
+
+ measurement := jobNameClean + "_checkins"
+ // Create a point and add to batch
+ tags := map[string]string{}
+ fields := map[string]interface{}{"check-in": 1}
+ pt, err := client.NewPoint(measurement, tags, fields)
+ if err != nil {
+ return "", err
+ }
+ bp.AddPoint(pt)
+
+ // Write the batch
+ err = c.Write(bp)
+ if err != nil {
+ return "", err
+ }
+ return measurement, nil
+}
+
+// Sanitize jobname - we only want letters, numbers, and underscores
+func sanitizeJobName(jobName string) (string, error) {
+ // remove any non-alphanumeric chars, and non-space chars
+ reg1, err := regexp.Compile("[^ _a-zA-Z0-9]+")
+ if err != nil {
+ return "", nil
+ }
+ jobName = reg1.ReplaceAllString(jobName, "")
+ // replace spaces with _
+ reg2, err := regexp.Compile("[ ]+")
+ if err != nil {
+ return "", nil
+ }
+ jobName = reg2.ReplaceAllString(jobName, "_")
+ return strings.ToLower(jobName), nil
+}
+
+// Every new endpoint/package MUST implement this function like so...
+func MakeHTTPHandler(endpoint string) http.Handler {
+ return common.MakeHTTPHandler(endpoint, jobCheckinHandler)
+}