@@ -143,7 +143,7 @@ type compiledMetric interface {
143
143
Type () metric.Type
144
144
}
145
145
146
- // newCompiledMetric returns a compiledMetric depending given the metric type.
146
+ // newCompiledMetric returns a compiledMetric depending on the given metric type.
147
147
func newCompiledMetric (m Metric ) (compiledMetric , error ) {
148
148
switch m .Type {
149
149
case MetricTypeGauge :
@@ -217,7 +217,41 @@ func (c *compiledGauge) Values(v interface{}) (result []eachValue, errs []error)
217
217
switch iter := v .(type ) {
218
218
case map [string ]interface {}:
219
219
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
+ }
221
255
if err != nil {
222
256
onError (fmt .Errorf ("[%s]: %w" , key , err ))
223
257
continue
@@ -387,7 +421,19 @@ func less(a, b map[string]string) bool {
387
421
388
422
func (c compiledGauge ) value (it interface {}) (* eachValue , error ) {
389
423
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 )
391
437
if err != nil {
392
438
return nil , fmt .Errorf ("%s: %w" , c .ValueFrom , err )
393
439
}
@@ -554,7 +600,10 @@ func compilePath(path []string) (out valuePath, _ error) {
554
600
} else if s , ok := m .([]interface {}); ok {
555
601
i , err := strconv .Atoi (part )
556
602
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
558
607
}
559
608
if i < 0 {
560
609
// negative index
0 commit comments