Skip to content

Commit 757055d

Browse files
authored
Merge pull request #1958 from rexagod/1947
Handle unit length `valueFrom` values
2 parents f27c1ef + 1032430 commit 757055d

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

pkg/customresourcestate/registry_factory.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ type compiledMetric interface {
143143
Type() metric.Type
144144
}
145145

146-
// newCompiledMetric returns a compiledMetric depending given the metric type.
146+
// newCompiledMetric returns a compiledMetric depending on the given metric type.
147147
func newCompiledMetric(m Metric) (compiledMetric, error) {
148148
switch m.Type {
149149
case MetricTypeGauge:
@@ -217,7 +217,41 @@ func (c *compiledGauge) Values(v interface{}) (result []eachValue, errs []error)
217217
switch iter := v.(type) {
218218
case map[string]interface{}:
219219
for key, it := range iter {
220-
ev, err := c.value(it)
220+
// TODO: Handle multi-length valueFrom paths (https://github.com/kubernetes/kube-state-metrics/pull/1958#discussion_r1099243161).
221+
// Try to deduce `valueFrom`'s value from the current element.
222+
var ev *eachValue
223+
var err error
224+
var didResolveValueFrom bool
225+
// `valueFrom` will ultimately be rendered into a string and sent to the fallback in place, which also expects a string.
226+
// So we'll do the same and operate on the string representation of `valueFrom`'s value.
227+
sValueFrom := c.ValueFrom.String()
228+
// No comma means we're looking at a unit-length path (in an array).
229+
if !strings.Contains(sValueFrom, ",") &&
230+
sValueFrom[0] == '[' && sValueFrom[len(sValueFrom)-1] == ']' &&
231+
// "[...]" and not "[]".
232+
len(sValueFrom) > 2 {
233+
extractedValueFrom := sValueFrom[1 : len(sValueFrom)-1]
234+
if key == extractedValueFrom {
235+
gotFloat, err := toFloat64(it, c.NilIsZero)
236+
if err != nil {
237+
onError(fmt.Errorf("[%s]: %w", key, err))
238+
continue
239+
}
240+
labels := make(map[string]string)
241+
ev = &eachValue{
242+
Labels: labels,
243+
Value: gotFloat,
244+
}
245+
didResolveValueFrom = true
246+
}
247+
}
248+
// Fallback to the regular path resolution, if we didn't manage to resolve `valueFrom`'s value.
249+
if !didResolveValueFrom {
250+
ev, err = c.value(it)
251+
if ev == nil {
252+
continue
253+
}
254+
}
221255
if err != nil {
222256
onError(fmt.Errorf("[%s]: %w", key, err))
223257
continue
@@ -387,7 +421,19 @@ func less(a, b map[string]string) bool {
387421

388422
func (c compiledGauge) value(it interface{}) (*eachValue, error) {
389423
labels := make(map[string]string)
390-
value, err := toFloat64(c.ValueFrom.Get(it), c.NilIsZero)
424+
got := c.ValueFrom.Get(it)
425+
// If `valueFrom` was not resolved, respect `NilIsZero` and return.
426+
if got == nil {
427+
if c.NilIsZero {
428+
return &eachValue{
429+
Labels: labels,
430+
Value: 0,
431+
}, nil
432+
}
433+
// Don't error if there was not a type-casting issue (`toFloat64`), but rather a failed lookup.
434+
return nil, nil
435+
}
436+
value, err := toFloat64(got, c.NilIsZero)
391437
if err != nil {
392438
return nil, fmt.Errorf("%s: %w", c.ValueFrom, err)
393439
}
@@ -554,7 +600,10 @@ func compilePath(path []string) (out valuePath, _ error) {
554600
} else if s, ok := m.([]interface{}); ok {
555601
i, err := strconv.Atoi(part)
556602
if err != nil {
557-
return fmt.Errorf("invalid list index: %s", part)
603+
// This means we are here: [ <string>, <int>, ... ] (eg., [ "foo", "0", ... ], i.e., <path>.foo[0]...
604+
// ^
605+
// Skip over.
606+
return nil
558607
}
559608
if i < 0 {
560609
// negative index

pkg/customresourcestate/registry_factory_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,17 @@ func Test_values(t *testing.T) {
183183
newEachValue(t, 2, "type", "type-a", "active", "1"),
184184
newEachValue(t, 4, "type", "type-b", "active", "3"),
185185
}},
186+
{name: "path-relative valueFrom value", each: &compiledGauge{
187+
compiledCommon: compiledCommon{
188+
path: mustCompilePath(t, "metadata"),
189+
labelFromPath: map[string]valuePath{
190+
"name": mustCompilePath(t, "name"),
191+
},
192+
},
193+
ValueFrom: mustCompilePath(t, "creationTimestamp"),
194+
}, wantResult: []eachValue{
195+
newEachValue(t, 1.6563744e+09),
196+
}},
186197
{name: "array", each: &compiledGauge{
187198
compiledCommon: compiledCommon{
188199
path: mustCompilePath(t, "status", "condition_values"),

0 commit comments

Comments
 (0)