@@ -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.
147147func 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
388422func (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
0 commit comments