Skip to content

Commit

Permalink
Merge pull request #1989 from mrueg/crs-support-quantity
Browse files Browse the repository at this point in the history
feat(CustomResourceState): Support quantities and percentages
  • Loading branch information
k8s-ci-robot authored Mar 1, 2023
2 parents 3a7e617 + d799f40 commit 054f2a4
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/customresourcestate-metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ Supported types are:
* for string the following logic applies
* `"true"` and `"yes"` are mapped to `1.0` and `"false"` and `"no"` are mapped to `0.0` (all case insensitive)
* RFC3339 times are parsed to float timestamp
* Quantities like "250m" or "512Gi" are parsed to float using https://github.com/kubernetes/apimachinery/blob/master/pkg/api/resource/quantity.go
* Percentages ending with a "%" are parsed to float
* finally the string is parsed to float using https://pkg.go.dev/strconv#ParseFloat which should support all common number formats. If that fails an error is yielded

##### Example for status conditions on Kubernetes Controllers
Expand Down
14 changes: 14 additions & 0 deletions pkg/customresourcestate/registry_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import (
"strings"
"time"

"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/klog/v2"

"k8s.io/kube-state-metrics/v2/pkg/metric"
Expand Down Expand Up @@ -690,16 +692,28 @@ func toFloat64(value interface{}, nilIsZero bool) (float64, error) {
}
return 0, nil
case string:
// The string contains a boolean as a string
normalized := strings.ToLower(value.(string))
if normalized == "true" || normalized == "yes" {
return 1, nil
}
if normalized == "false" || normalized == "no" {
return 0, nil
}
// The string contains a RFC3339 timestamp
if t, e := time.Parse(time.RFC3339, value.(string)); e == nil {
return float64(t.Unix()), nil
}
// The string contains a quantity with a suffix like "25m" (milli) or "5Gi" (binarySI)
if t, e := resource.ParseQuantity(value.(string)); e == nil {
return t.AsApproximateFloat64(), nil
}
// The string contains a percentage with a suffix "%"
if e := validation.IsValidPercent(value.(string)); len(e) == 0 {
t, e := strconv.ParseFloat(strings.TrimRight(value.(string), "%"), 64)
return t / 100, e
}

return strconv.ParseFloat(value.(string), 64)
case byte:
v = float64(vv)
Expand Down
38 changes: 37 additions & 1 deletion pkg/customresourcestate/registry_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ func init() {
"ready": 4,
},
},
"uptime": 43.21,
"uptime": 43.21,
"quantity_milli": "250m",
"quantity_binarySI": "5Gi",
"percentage": "28%",
"condition_values": Array{
Obj{
"name": "a",
Expand Down Expand Up @@ -95,6 +98,7 @@ func init() {
"qux": "quxx",
"bar": "baz",
},
"percentage": "39%",
"creationTimestamp": "2022-06-28T00:00:00Z",
},
})
Expand Down Expand Up @@ -225,6 +229,38 @@ func Test_values(t *testing.T) {
}, wantResult: []eachValue{
newEachValue(t, 1656374400),
}},
{name: "quantity_milli", each: &compiledGauge{
compiledCommon: compiledCommon{
path: mustCompilePath(t, "status", "quantity_milli"),
},
}, wantResult: []eachValue{
newEachValue(t, 0.25),
}},
{name: "quantity_binarySI", each: &compiledGauge{
compiledCommon: compiledCommon{
path: mustCompilePath(t, "status", "quantity_binarySI"),
},
}, wantResult: []eachValue{
newEachValue(t, 5.36870912e+09),
}},
{name: "percentage", each: &compiledGauge{
compiledCommon: compiledCommon{
path: mustCompilePath(t, "status", "percentage"),
},
}, wantResult: []eachValue{
newEachValue(t, 0.28),
}},
{name: "path-relative valueFrom percentage", each: &compiledGauge{
compiledCommon: compiledCommon{
path: mustCompilePath(t, "metadata"),
labelFromPath: map[string]valuePath{
"name": mustCompilePath(t, "name"),
},
},
ValueFrom: mustCompilePath(t, "percentage"),
}, wantResult: []eachValue{
newEachValue(t, 0.39, "name", "foo"),
}},
{name: "boolean_string", each: &compiledGauge{
compiledCommon: compiledCommon{
path: mustCompilePath(t, "spec", "paused"),
Expand Down

0 comments on commit 054f2a4

Please sign in to comment.