package main import ( "encoding/json" "flag" "fmt" "log" "net/http" "strconv" "time" ) var ( listen = flag.String("l", "0.0.0.0:8080", "Listen address") rate = flag.Int("r", 500, "Sample rate (ms)") timeout = flag.Int("t", 10, "Total timeout (s)") minRate = 500 maxTimeout = 600 ) // TODO: bench test with pointer func getMetrics() (Metric, error) { log.Println("Started getMetrics") m := Metric{} err := m.getMemory() if err != nil { log.Printf("Cannot get memory: %s\n", err) return m, err } err = m.getLoadAvg() if err != nil { log.Printf("Cannot get load average: %s\n", err) return m, err } err = m.getCpuMetric() if err != nil { log.Printf("Cannot get CPU metrics: %s\n", err) return m, err } err = m.getDiskMetrics() if err != nil { log.Printf("Cannot get disk metrics: %s\n", err) return m, err } err = m.getNetworkMetrics() if err != nil { log.Printf("Cannot get network metrics: %s\n", err) return m, err } return m, nil } func (t *Test) collectData() { timeo := time.After(t.Timeout) t.Status = "STARTED" for { select { case tick := <-t.ticker.C: t.Status = "ONGOING" m, err := getMetrics() if err != nil { log.Printf("Cannot collect metrics: %s\n", err) return } t.Result = append(t.Result, m) log.Printf("%v: %v\n", tick, m) //t.Result += fmt.Sprintf("%v: Collecting data\n", tick) case <-t.Done: log.Printf("DONE\n") t.Status = "COMPLETED" t.Unlock() return case <-timeo: log.Printf("TIMEOUT\n") t.Status = "TIMEOUT" t.Unlock() return } } return // really needed? } func (t *Test) startTest(w http.ResponseWriter, req *http.Request) { // Check if another collectData is running (better: check sync.Mutex) if t.Status == "ONGOING" { http.Error(w, "Another test is currently ongoing, stop or try again later", 409) //better error code return } // Check parameters qRate := req.URL.Query().Get("rate") if qRate != "" { qRate, err := strconv.Atoi(qRate) if err != nil { log.Printf("Error while converting rate: %s\n", err.Error()) http.Error(w, "Error: error while converting rate", 400) return } if qRate < minRate { log.Printf("Requested rate is too low: %d (min: %d)\n", qRate, minRate) http.Error(w, "Error: requested rate too low", 400) return } t.Rate = qRate } qTimeout := req.URL.Query().Get("timeout") if qTimeout != "" { qTimeout, err := strconv.Atoi(qTimeout) if err != nil { log.Printf("Error while converting timeout: %s\n", err.Error()) http.Error(w, "Error: error while converting timeout", 400) return } if qTimeout > maxTimeout { log.Printf("Requested timeout is too high: %d (max: %d)\n", qTimeout, maxTimeout) http.Error(w, "Error: requested timeout too high", 400) return } t.Timeout = time.Duration(qTimeout) * time.Second } qName := req.URL.Query().Get("name") if qName == "" { t.Name = fmt.Sprintf("test-%d", time.Now().Local().Unix()) } else { t.Name = qName } // Check if previous test is present if len(t.Result) == 0 { log.Println("Cleaning previous test data") t.Result = []Metric{} } // Lock to avoid multiple start t.Lock() fmt.Fprintf(w, "Test name: %s, rate: %d (ms), timeout: %d (s)\n", t.Name, t.Rate, t.Timeout/1000000000) endTime := time.Now().Local().Add(t.Timeout) fmt.Fprintf(w, "Test will end @%v\n", endTime.Format(time.RFC1123Z)) go t.collectData() } func (t *Test) stopTest(w http.ResponseWriter, req *http.Request) { if t.Status != "ONGOING" { e := fmt.Sprintf("Cannot stop test in %s status: try start first", t.Status) http.Error(w, e, 400) return } t.ticker.Stop() t.Done <- true fmt.Fprintf(w, "Done\n") fmt.Fprintf(w, "%v\n", t.Result) } func (t *Test) testStatus(w http.ResponseWriter, req *http.Request) { qFormat := req.URL.Query().Get("format") if qFormat == "json" { b, err := json.Marshal(t.Result) if err != nil { log.Printf("Cannot marshal json: %s\n", err) http.Error(w, err.Error(), 500) return } fmt.Fprintf(w, "%s\n", b) } else { fmt.Fprintf(w, "%v\n", t.Result) fmt.Fprintf(w, "Test %s, status: %s\n", t.Name, t.Status) } } func newTest() *Test { log.Println("Creating new test") // Not checking if rate and timeout are in value range because are set on CLI t := Test{ ticker: time.NewTicker(time.Duration(*rate) * time.Millisecond), Done: make(chan bool), Rate: *rate, Timeout: time.Duration(*timeout) * time.Second, Status: "CREATED", } return &t } func main() { flag.Parse() t := newTest() log.Printf("%+v\n", t) http.HandleFunc("/start", t.startTest) http.HandleFunc("/status", t.testStatus) http.HandleFunc("/stop", t.stopTest) log.Printf("Listening on %s\n", *listen) log.Fatal(http.ListenAndServe(*listen, nil)) }