diff --git a/config/config.go b/config/config.go index 290fa2d9c..01dfee3b2 100644 --- a/config/config.go +++ b/config/config.go @@ -17,6 +17,7 @@ import ( "errors" "fmt" "github.com/prometheus/prometheus/model" + "github.com/prometheus/prometheus/utility" "time" ) @@ -55,7 +56,7 @@ func (config *Config) AddJob(options map[string]string, targets []Targets) error return errors.New("Missing job name") } if len(targets) == 0 { - return errors.New(fmt.Sprintf("No targets configured for job '%v'", name)) + return fmt.Errorf("No targets configured for job '%v'", name) } job := &JobConfig{ Targets: tmpJobTargets, @@ -69,18 +70,18 @@ func (config *Config) AddJob(options map[string]string, targets []Targets) error return nil } -func (config *GlobalConfig) SetOption(option string, value string) error { +func (config *GlobalConfig) SetOption(option string, value string) (err error) { switch option { case "scrape_interval": - config.ScrapeInterval = stringToDuration(value) + config.ScrapeInterval, err = utility.StringToDuration(value) return nil case "evaluation_interval": - config.EvaluationInterval = stringToDuration(value) - return nil + config.EvaluationInterval, err = utility.StringToDuration(value) + return err default: - return errors.New(fmt.Sprintf("Unrecognized global configuration option '%v'", option)) + err = fmt.Errorf("Unrecognized global configuration option '%v'", option) } - return nil + return } func (config *GlobalConfig) SetLabels(labels model.LabelSet) { @@ -95,18 +96,16 @@ func (config *GlobalConfig) AddRuleFiles(ruleFiles []string) { } } -func (job *JobConfig) SetOption(option string, value string) error { +func (job *JobConfig) SetOption(option string, value string) (err error) { switch option { case "name": job.Name = value - return nil case "scrape_interval": - job.ScrapeInterval = stringToDuration(value) - return nil + job.ScrapeInterval, err = utility.StringToDuration(value) default: - return errors.New(fmt.Sprintf("Unrecognized job configuration option '%v'", option)) + err = fmt.Errorf("Unrecognized job configuration option '%v'", option) } - return nil + return } func (job *JobConfig) AddTargets(endpoints []string, labels model.LabelSet) { diff --git a/config/helpers.go b/config/helpers.go index af7251969..8ef989345 100644 --- a/config/helpers.go +++ b/config/helpers.go @@ -17,9 +17,6 @@ import ( "fmt" "github.com/prometheus/prometheus/model" "log" - "regexp" - "strconv" - "time" ) // Unfortunately, more global variables that are needed for parsing. @@ -30,7 +27,9 @@ var tmpTargetLabels = model.LabelSet{} func configError(error string, v ...interface{}) { message := fmt.Sprintf(error, v...) - log.Fatal(fmt.Sprintf("Line %v, char %v: %s", yyline, yypos, message)) + // TODO: Don't just die here. Pass errors back all the way to the caller + // instead. + log.Fatalf("Line %v, char %v: %s", yyline, yypos, message) } func PushJobOption(option string, value string) { @@ -66,26 +65,3 @@ func PopJob() { tmpJobOptions = map[string]string{} tmpJobTargets = []Targets{} } - -func stringToDuration(durationStr string) time.Duration { - durationRE := regexp.MustCompile("^([0-9]+)([ywdhms]+)$") - matches := durationRE.FindStringSubmatch(durationStr) - if len(matches) != 3 { - configError("Not a valid duration string: '%v'", durationStr) - } - value, _ := strconv.Atoi(matches[1]) - unit := matches[2] - switch unit { - case "y": - value *= 60 * 60 * 24 * 365 - case "w": - value *= 60 * 60 * 24 * 7 - case "d": - value *= 60 * 60 * 24 - case "h": - value *= 60 * 60 - case "m": - value *= 60 - } - return time.Duration(value) * time.Second -} diff --git a/config/printer.go b/config/printer.go index 26d7c6bc1..00e4c7dc1 100644 --- a/config/printer.go +++ b/config/printer.go @@ -16,6 +16,7 @@ package config import ( "fmt" "github.com/prometheus/prometheus/model" + "github.com/prometheus/prometheus/utility" "strings" ) @@ -47,8 +48,8 @@ func labelsToString(indent int, labels model.LabelSet) string { func (global *GlobalConfig) ToString(indent int) string { str := indentStr(indent, "global {\n") - str += indentStr(indent+1, "scrape_interval = \"%vs\"\n", global.ScrapeInterval) - str += indentStr(indent+1, "evaluation_interval = \"%vs\"\n", global.EvaluationInterval) + str += indentStr(indent+1, "scrape_interval = \"%s\"\n", utility.DurationToString(global.ScrapeInterval)) + str += indentStr(indent+1, "evaluation_interval = \"%s\"\n", utility.DurationToString(global.EvaluationInterval)) str += labelsToString(indent+1, global.Labels) str += indentStr(indent, "}\n") str += indentStr(indent+1, "rule_files = [\n") @@ -61,9 +62,8 @@ func (global *GlobalConfig) ToString(indent int) string { func (job *JobConfig) ToString(indent int) string { str := indentStr(indent, "job {\n") - str += indentStr(indent+1, "job {\n") str += indentStr(indent+1, "name = \"%v\"\n", job.Name) - str += indentStr(indent+1, "scrape_interval = \"%vs\"\n", job.ScrapeInterval) + str += indentStr(indent+1, "scrape_interval = \"%s\"\n", utility.DurationToString(job.ScrapeInterval)) for _, targets := range job.Targets { str += indentStr(indent+1, "targets {\n") str += indentStr(indent+2, "endpoints = [\n") diff --git a/rules/ast/printer.go b/rules/ast/printer.go index 1f38117ac..4fd16296d 100644 --- a/rules/ast/printer.go +++ b/rules/ast/printer.go @@ -16,6 +16,7 @@ package ast import ( "encoding/json" "fmt" + "github.com/prometheus/prometheus/utility" "sort" "strings" "time" @@ -65,29 +66,6 @@ func exprTypeToString(exprType ExprType) string { return exprTypeMap[exprType] } -func durationToString(duration time.Duration) string { - seconds := int64(duration / time.Second) - factors := map[string]int64{ - "y": 60 * 60 * 24 * 365, - "d": 60 * 60 * 24, - "h": 60 * 60, - "m": 60, - "s": 1, - } - unit := "s" - switch int64(0) { - case seconds % factors["y"]: - unit = "y" - case seconds % factors["d"]: - unit = "d" - case seconds % factors["h"]: - unit = "h" - case seconds % factors["m"]: - unit = "m" - } - return fmt.Sprintf("%v%v", seconds/factors[unit], unit) -} - func (vector Vector) ToString() string { metricStrings := []string{} for _, sample := range vector { @@ -228,7 +206,7 @@ func (node *VectorLiteral) ToString() string { func (node *MatrixLiteral) ToString() string { vectorString := (&VectorLiteral{labels: node.labels}).ToString() - intervalString := fmt.Sprintf("['%v']", durationToString(node.interval)) + intervalString := fmt.Sprintf("['%v']", utility.DurationToString(node.interval)) return vectorString + intervalString } diff --git a/rules/helpers.go b/rules/helpers.go index 3008768d1..a5a224c44 100644 --- a/rules/helpers.go +++ b/rules/helpers.go @@ -14,48 +14,15 @@ package rules import ( - "errors" "fmt" "github.com/prometheus/prometheus/model" "github.com/prometheus/prometheus/rules/ast" - "regexp" - "strconv" - "time" + "github.com/prometheus/prometheus/utility" ) -func rulesError(error string, v ...interface{}) error { - return errors.New(fmt.Sprintf(error, v...)) -} - -// TODO move to common place, currently duplicated in config/ -func stringToDuration(durationStr string) (time.Duration, error) { - durationRE := regexp.MustCompile("^([0-9]+)([ywdhms]+)$") - matches := durationRE.FindStringSubmatch(durationStr) - if len(matches) != 3 { - return 0, rulesError("Not a valid duration string: '%v'", durationStr) - } - value, _ := strconv.Atoi(matches[1]) - unit := matches[2] - switch unit { - case "y": - value *= 60 * 60 * 24 * 365 - case "w": - value *= 60 * 60 * 24 - case "d": - value *= 60 * 60 * 24 - case "h": - value *= 60 * 60 - case "m": - value *= 60 - case "s": - value *= 1 - } - return time.Duration(value) * time.Second, nil -} - func CreateRule(name string, labels model.LabelSet, root ast.Node, permanent bool) (*Rule, error) { if root.Type() != ast.VECTOR { - return nil, rulesError("Rule %v does not evaluate to vector type", name) + return nil, fmt.Errorf("Rule %v does not evaluate to vector type", name) } return NewRule(name, labels, root.(ast.VectorNode), permanent), nil } @@ -63,18 +30,18 @@ func CreateRule(name string, labels model.LabelSet, root ast.Node, permanent boo func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) { function, err := ast.GetFunction(name) if err != nil { - return nil, rulesError("Unknown function \"%v\"", name) + return nil, fmt.Errorf("Unknown function \"%v\"", name) } functionCall, err := ast.NewFunctionCall(function, args) if err != nil { - return nil, rulesError(err.Error()) + return nil, fmt.Errorf(err.Error()) } return functionCall, nil } func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy []model.LabelName) (*ast.VectorAggregation, error) { if vector.Type() != ast.VECTOR { - return nil, rulesError("Operand of %v aggregation must be of vector type", aggrTypeStr) + return nil, fmt.Errorf("Operand of %v aggregation must be of vector type", aggrTypeStr) } var aggrTypes = map[string]ast.AggrType{ "SUM": ast.SUM, @@ -84,7 +51,7 @@ func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy []model.L } aggrType, ok := aggrTypes[aggrTypeStr] if !ok { - return nil, rulesError("Unknown aggregation type '%v'", aggrTypeStr) + return nil, fmt.Errorf("Unknown aggregation type '%v'", aggrTypeStr) } return ast.NewVectorAggregation(aggrType, vector.(ast.VectorNode), groupBy), nil } @@ -107,11 +74,11 @@ func NewArithExpr(opTypeStr string, lhs ast.Node, rhs ast.Node) (ast.Node, error } opType, ok := opTypes[opTypeStr] if !ok { - return nil, rulesError("Invalid binary operator \"%v\"", opTypeStr) + return nil, fmt.Errorf("Invalid binary operator \"%v\"", opTypeStr) } expr, err := ast.NewArithExpr(opType, lhs, rhs) if err != nil { - return nil, rulesError(err.Error()) + return nil, fmt.Errorf(err.Error()) } return expr, nil } @@ -123,9 +90,9 @@ func NewMatrix(vector ast.Node, intervalStr string) (ast.MatrixNode, error) { break } default: - return nil, rulesError("Intervals are currently only supported for vector literals.") + return nil, fmt.Errorf("Intervals are currently only supported for vector literals.") } - interval, err := stringToDuration(intervalStr) + interval, err := utility.StringToDuration(intervalStr) if err != nil { return nil, err } diff --git a/rules/lexer.l b/rules/lexer.l index f174f0bbf..643f52fc6 100644 --- a/rules/lexer.l +++ b/rules/lexer.l @@ -48,7 +48,9 @@ AVG|SUM|MAX|MIN { yylval.str = yytext; return AGGR_OP } {L}({L}|{D})+ { yylval.str = yytext; return IDENTIFIER } \-?{D}+(\.{D}*)? { num, err := strconv.ParseFloat(yytext, 32); - if (err != nil) { rulesError("Invalid float %v", yytext) } + if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) { + panic("Invalid float") + } yylval.num = model.SampleValue(num) return NUMBER } diff --git a/rules/lexer.l.go b/rules/lexer.l.go index aceeaec4a..7ec712fd6 100644 --- a/rules/lexer.l.go +++ b/rules/lexer.l.go @@ -436,8 +436,8 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon }() { num, err := strconv.ParseFloat(yytext, 32) - if err != nil { - rulesError("Invalid float %v", yytext) + if err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax { + panic("Invalid float") } yylval.num = model.SampleValue(num) return yyactionreturn{NUMBER, yyRT_USER_RETURN} diff --git a/utility/strconv.go b/utility/strconv.go new file mode 100644 index 000000000..655395f87 --- /dev/null +++ b/utility/strconv.go @@ -0,0 +1,72 @@ +// Copyright 2013 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utility + +import ( + "fmt" + "regexp" + "strconv" + "time" +) + +var durationRE = regexp.MustCompile("^([0-9]+)([ywdhms]+)$") + +func DurationToString(duration time.Duration) string { + seconds := int64(duration / time.Second) + factors := map[string]int64{ + "y": 60 * 60 * 24 * 365, + "d": 60 * 60 * 24, + "h": 60 * 60, + "m": 60, + "s": 1, + } + unit := "s" + switch int64(0) { + case seconds % factors["y"]: + unit = "y" + case seconds % factors["d"]: + unit = "d" + case seconds % factors["h"]: + unit = "h" + case seconds % factors["m"]: + unit = "m" + } + return fmt.Sprintf("%v%v", seconds/factors[unit], unit) +} + +func StringToDuration(durationStr string) (duration time.Duration, err error) { + matches := durationRE.FindStringSubmatch(durationStr) + if len(matches) != 3 { + err = fmt.Errorf("Not a valid duration string: '%v'", durationStr) + return + } + durationSeconds, _ := strconv.Atoi(matches[1]) + duration = time.Duration(durationSeconds) * time.Second + unit := matches[2] + switch unit { + case "y": + duration *= 60 * 60 * 24 * 365 + case "w": + duration *= 60 * 60 * 24 * 7 + case "d": + duration *= 60 * 60 * 24 + case "h": + duration *= 60 * 60 + case "m": + duration *= 60 + case "s": + duration *= 1 + } + return +}