Example #1
0
// Ensure a time duration can be parsed.
func TestParseDuration(t *testing.T) {
	var tests = []struct {
		s   string
		d   time.Duration
		err string
	}{
		{s: `3`, d: 3 * time.Microsecond},
		{s: `1000`, d: 1000 * time.Microsecond},
		{s: `10u`, d: 10 * time.Microsecond},
		{s: `10ยต`, d: 10 * time.Microsecond},
		{s: `15ms`, d: 15 * time.Millisecond},
		{s: `100s`, d: 100 * time.Second},
		{s: `2m`, d: 2 * time.Minute},
		{s: `2h`, d: 2 * time.Hour},
		{s: `2d`, d: 2 * 24 * time.Hour},
		{s: `2w`, d: 2 * 7 * 24 * time.Hour},

		{s: ``, err: "invalid duration"},
		{s: `w`, err: "invalid duration"},
		{s: `1.2w`, err: "invalid duration"},
		{s: `10x`, err: "invalid duration"},
	}

	for i, tt := range tests {
		d, err := influxql.ParseDuration(tt.s)
		if !reflect.DeepEqual(tt.err, errstring(err)) {
			t.Errorf("%d. %q: error mismatch:\n  exp=%s\n  got=%s\n\n", i, tt.s, tt.err, err)
		} else if tt.d != d {
			t.Errorf("%d. %q\n\nduration mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.s, tt.d, d)
		}
	}
}
Example #2
0
// create a new number from a text string
func newDur(p int, text string) (*DurationNode, error) {
	n := &DurationNode{
		pos: pos(p),
	}
	d, err := influxql.ParseDuration(text)
	if err != nil {
		return nil, err
	}
	n.Dur = d
	return n, nil
}
Example #3
0
func (ts *Service) handleSave(w http.ResponseWriter, r *http.Request) {
	name := r.URL.Query().Get("name")
	newTask := &rawTask{
		Name:             name,
		SnapshotInterval: ts.snapshotInterval,
	}

	// Check for existing task
	raw, err := ts.LoadRaw(name)
	exists := err == nil
	if exists {
		newTask = raw
	}

	// Get task type
	ttStr := r.URL.Query().Get("type")
	switch ttStr {
	case "stream":
		newTask.Type = kapacitor.StreamTask
	case "batch":
		newTask.Type = kapacitor.BatchTask
	default:
		if !exists {
			if ttStr == "" {
				httpd.HttpError(w, fmt.Sprintf("no task with name %q exists cannot infer type.", name), true, http.StatusBadRequest)
			} else {
				httpd.HttpError(w, fmt.Sprintf("unknown type %q", ttStr), true, http.StatusBadRequest)
			}
			return
		}
	}

	// Get tick script
	tick, err := ioutil.ReadAll(r.Body)
	if err != nil {
		httpd.HttpError(w, err.Error(), true, http.StatusBadRequest)
		return
	}
	if len(tick) > 0 {
		newTask.TICKscript = string(tick)
	} else if !exists {
		httpd.HttpError(w, fmt.Sprintf("must provide TICKscript via POST data."), true, http.StatusBadRequest)
		return
	}

	// Get dbrps
	dbrpsStr := r.URL.Query().Get("dbrps")
	if dbrpsStr != "" {
		dbrps := make([]kapacitor.DBRP, 0)
		err = json.Unmarshal([]byte(dbrpsStr), &dbrps)
		if err != nil {
			httpd.HttpError(w, err.Error(), true, http.StatusBadRequest)
			return
		}
		newTask.DBRPs = dbrps
	} else if !exists {
		httpd.HttpError(w, fmt.Sprintf("must provide at least one database and retention policy."), true, http.StatusBadRequest)
		return
	}

	// Get snapshot interval
	snapshotIntervalStr := r.URL.Query().Get("snapshot")
	if snapshotIntervalStr != "" {
		snapshotInterval, err := influxql.ParseDuration(snapshotIntervalStr)
		if err != nil {
			httpd.HttpError(w, err.Error(), true, http.StatusBadRequest)
			return
		}
		newTask.SnapshotInterval = snapshotInterval
	}

	err = ts.Save(newTask)
	if err != nil {
		httpd.HttpError(w, err.Error(), true, http.StatusInternalServerError)
		return
	}
}
Example #4
0
func (r *Service) handleRecord(w http.ResponseWriter, req *http.Request) {
	type doFunc func() error
	var doF doFunc
	started := make(chan struct{})

	rid := uuid.NewV4()
	typ := req.URL.Query().Get("type")
	switch typ {
	case "stream":
		task := req.URL.Query().Get("name")
		if task == "" {
			httpd.HttpError(w, "no task specified", true, http.StatusBadRequest)
			return
		}
		t, err := r.TaskStore.Load(task)
		if err != nil {
			httpd.HttpError(w, err.Error(), true, http.StatusNotFound)
			return
		}

		durStr := req.URL.Query().Get("duration")
		dur, err := influxql.ParseDuration(durStr)
		if err != nil {
			httpd.HttpError(w, "invalid duration string: "+err.Error(), true, http.StatusBadRequest)
			return
		}

		doF = func() error {
			err := r.doRecordStream(rid, dur, t.DBRPs, started)
			if err != nil {
				close(started)
			}
			return err
		}

	case "batch":
		var err error

		// Determine start time.
		var start time.Time
		startStr := req.URL.Query().Get("start")
		pastStr := req.URL.Query().Get("past")
		if startStr != "" && pastStr != "" {
			httpd.HttpError(w, "must not pass both 'start' and 'past' parameters", true, http.StatusBadRequest)
			return
		}

		now := time.Now()

		switch {
		case startStr != "":
			start, err = time.Parse(time.RFC3339, startStr)
			if err != nil {
				httpd.HttpError(w, err.Error(), true, http.StatusBadRequest)
				return
			}
		case pastStr != "":
			diff, err := influxql.ParseDuration(pastStr)
			if err != nil {
				httpd.HttpError(w, err.Error(), true, http.StatusBadRequest)
				return
			}
			start = now.Add(-1 * diff)
		}

		// Get stop time, if present
		stop := now
		stopStr := req.URL.Query().Get("stop")
		if stopStr != "" {
			stop, err = time.Parse(time.RFC3339, stopStr)
			if err != nil {
				httpd.HttpError(w, err.Error(), true, http.StatusBadRequest)
				return
			}
		}

		// Get task
		task := req.URL.Query().Get("name")
		if task == "" {
			httpd.HttpError(w, "no task specified", true, http.StatusBadRequest)
			return
		}

		t, err := r.TaskStore.Load(task)
		if err != nil {
			httpd.HttpError(w, err.Error(), true, http.StatusNotFound)
			return
		}

		doF = func() error {
			close(started)
			return r.doRecordBatch(rid, t, start, stop)
		}
	case "query":
		query := req.URL.Query().Get("query")
		if query == "" {
			httpd.HttpError(w, "must pass query parameter", true, http.StatusBadRequest)
			return
		}

		typeStr := req.URL.Query().Get("ttype")
		var tt kapacitor.TaskType
		switch typeStr {
		case "stream":
			tt = kapacitor.StreamTask
		case "batch":
			tt = kapacitor.BatchTask
		default:
			httpd.HttpError(w, fmt.Sprintf("invalid type %q", typeStr), true, http.StatusBadRequest)
			return
		}
		doF = func() error {
			close(started)
			return r.doRecordQuery(rid, query, tt)
		}
	default:
		httpd.HttpError(w, "invalid recording type", true, http.StatusBadRequest)
		return
	}
	// Store recording in running recordings.
	errC := make(chan error, 1)
	func() {
		r.recordingsMu.Lock()
		defer r.recordingsMu.Unlock()
		r.runningRecordings[rid.String()] = errC
	}()

	// Spawn routine to perform actual recording.
	go func() {
		err := doF()
		if err != nil {
			// Always log an error since the user may not have requested the error.
			r.logger.Printf("E! recording %s failed: %v", rid.String(), err)
		}
		errC <- err
		// We have finished delete from running map
		r.recordingsMu.Lock()
		defer r.recordingsMu.Unlock()
		delete(r.runningRecordings, rid.String())
	}()

	// Wait till the goroutine for doing the recording has actually started
	<-started

	// Respond with the recording ID
	type response struct {
		RecordingID string `json:"RecordingID"`
	}
	w.Write(httpd.MarshalJSON(response{rid.String()}, true))
}