Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support list expansions #2068

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

rexagod
Copy link
Member

@rexagod rexagod commented May 15, 2023

What this PR does / why we need it: Allow CRS configuration to take in path values for lists, denoted by "*". This means it's possible to specify [..., <list>, "*", foo, ...] in the configuration to dynamically generate multiple metrics that reflect different states of foo in various list elements.

How does this change affect the cardinality of KSM: No change.

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. labels May 15, 2023
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rexagod

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels May 15, 2023
@rexagod rexagod force-pushed the support-list-expansions branch 2 times, most recently from 5c5949d to 5586928 Compare May 16, 2023 00:00
@rexagod rexagod marked this pull request as ready for review May 16, 2023 00:00
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 16, 2023
@rexagod rexagod force-pushed the support-list-expansions branch 2 times, most recently from 954a59a to 0eecd57 Compare May 16, 2023 00:15
rexagod referenced this pull request in rexagod/kube-state-metrics Jun 11, 2023
@CatherineF-dev
Copy link
Contributor

CatherineF-dev commented Jun 14, 2023

A related #1978

Current Custom State Resource API is a little complicated. Adding more features might bring more maintaining work and make it harder to migrate into cel.

I think cel can support this.

@dgrisonnet
Copy link
Member

I agree with Catherine, we shouldn't add more complexity to the existing config but rather focus on the new model.

@rexagod
Copy link
Member Author

rexagod commented Jun 14, 2023

So, are we sunsetting the older model?

@rexagod
Copy link
Member Author

rexagod commented Jun 14, 2023

Also, there was a thread regarding separating the CRS featureset into its own repository (the "non-turing" solution here being CEL-like languages, that do not offer an exhaustive way of defining the configuration, unlike go-based templating solutions).

@CatherineF-dev
Copy link
Contributor

CatherineF-dev commented Jun 14, 2023

Reuse the wheel (cel, a query model) can reduce our maintaining work on how to extract fields.
Then we can spend efforts on other things, instead of putting many efforts on implementing a query language.

@rexagod
Copy link
Member Author

rexagod commented Jun 14, 2023

Will this mean that KSM's config capabilities will be restricted to what the underlying solution (#1978) has to offer?

@CatherineF-dev
Copy link
Contributor

CatherineF-dev commented Jun 14, 2023

cel should be a complete language, which means it can extract every field.

cel is more powerful than current APIs and is capable of extracting more things.

@rexagod rexagod closed this Jun 14, 2023
@rexagod
Copy link
Member Author

rexagod commented Jun 14, 2023

@dgrisonnet I'm assuming going forward with the newer model will mean we'll deprecate the older one. So, with that in mind, are PRs such as this and others like: #2067, #2052 obsolete and should be closed?

@dgrisonnet
Copy link
Member

I need to catch up on the discussions, a lot happened when I was away, but my general feeling is that we are starting to make the configuration way too complex. My fear is that this is going to become too confusing for the users and I am also worries about the current state of the parser. Without a proper lexer/parser combination this is slowly becoming unmaintainable.

IMO with the state of things, we should rather focus on building new foundations for the config rather than making the existing one more complex since it will just make the migration process harder. wdyt?

@rexagod
Copy link
Member Author

rexagod commented Jun 14, 2023

I couldn't agree more.

The current CRS featureset, in addition to being complex and hardly maintainable for developers, doesn't have a clear, semantical, or easy-to-understand UX at the moment. Off the top of my mind, I can think of a few examples that go on to show a couple of such instances. I personally would not at all expect the user to be familiar with the following intricacies.

  • Relative paths can only be defined once, and the fact that technically there's two labelsFromPath possible but the other one does not support it.

           each:
             type: Info
             info:
               path: [spec, selector, labels]
               labelsFromPath:
                 "*": [matchLabels]
           labelsFromPath:
             name: [metadata, name]
  • The outer lablesFromPath operates on the subset that satified the inner labelsFromPath. In the configuration snippet below, the outer labelsFromPath will only generate name labels for all objects that satisfied the inner labelsFromPath, i.e., a non-nil foo label.

      	  each:
             type: Info
             info:
               path: [metadata]
               labelsFromPath:
                 "foo_label": [labels, foo]
           labelsFromPath:
             name: [metadata, name]
  • The difference in each of the configurations below, which looks more like a side-effect of another change.

     	  each:
             type: Info
             info:
               path: [users]
               labelsFromPath:
                 user: []

    outputs: kube_customresource_metric_name{..., user="user1"} 1 and kube_customresource_metric_name{..., user="user2"} 1 while,

     	  each:
             type: Info
             info:
               labelsFromPath:
                 user: [users]

    outputs: kube_customresource_metric_name{..., user="[user1 user2]"} 1.

TBH only reason I raised the aforementioned PRs was because I wasn't sure about the direction in which we were going to move forward over the last month regarding this, and assumed that doing so would entail discussions spanning a good while, so things would basically be done the same way as they were till now.

But, I'm enthusiastic and happy to see that there's a consensus on improving the current approach, and the discussions are yeilding actionable results. It'd be nice to see the proposed solution replacing as many configuration constructs as possible, in order to achieve a more accessible and deterministic UX for the user, thanks to @CatherineF-dev. 🎉

@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 29, 2023
@mrueg mrueg changed the title Support list expansions feat: Support list expansions Aug 29, 2023
@rexagod
Copy link
Member Author

rexagod commented Aug 29, 2023

(pulled in #2052 to combine all patches for the dynamic valueFrom)

Comment on lines 552 to 559
labelsFromPath:
# this can be combined with the wildcard prefixes feature introduced in #2052
"available_*": [available]
"lorem_*": [pending]
# this can be combined with dynamic valueFrom expressions introduced in #2068
valueFrom: [pending_resourceB]
# outputs:
...{...,available_resourceA="10",available_resourceB="20",...} ...
...{...,available_resourceA="10",available_resourceB="20",lorem_resourceA="0",lorem_resourceB="6"...} 6
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

valueFrom now allows users to utilise the generated wildcard labels as metric values.

@rexagod
Copy link
Member Author

rexagod commented Aug 30, 2023

/hold
Let me improve on this a bit (we should be able to replace the x_*: [y], valueFrom: [y_*] with x_*: [y], valueFrom: [x_*]).

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 30, 2023
@rexagod
Copy link
Member Author

rexagod commented Aug 30, 2023

/hold cancel
Up for reviews.

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 30, 2023
@k8s-triage-robot
Copy link

The Kubernetes project currently lacks enough contributors to adequately respond to all PRs.

This bot triages PRs according to the following rules:

  • After 90d of inactivity, lifecycle/stale is applied
  • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
  • After 30d of inactivity since lifecycle/rotten was applied, the PR is closed

You can:

  • Mark this PR as fresh with /remove-lifecycle stale
  • Close this PR with /close
  • Offer to help out with Issue Triage

Please send feedback to sig-contributor-experience at kubernetes/community.

/lifecycle stale

@k8s-ci-robot k8s-ci-robot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jan 22, 2024
@k8s-triage-robot
Copy link

The Kubernetes project currently lacks enough active contributors to adequately respond to all PRs.

This bot triages PRs according to the following rules:

  • After 90d of inactivity, lifecycle/stale is applied
  • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
  • After 30d of inactivity since lifecycle/rotten was applied, the PR is closed

You can:

  • Mark this PR as fresh with /remove-lifecycle rotten
  • Close this PR with /close
  • Offer to help out with Issue Triage

Please send feedback to sig-contributor-experience at kubernetes/community.

/lifecycle rotten

@k8s-ci-robot k8s-ci-robot added lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed. and removed lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. labels Feb 21, 2024
@rexagod
Copy link
Member Author

rexagod commented Feb 21, 2024

/remove-lifecycle rotten

@k8s-ci-robot k8s-ci-robot removed the lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed. label Feb 21, 2024
@rexagod
Copy link
Member Author

rexagod commented Mar 3, 2024

FYI This is up for reviews (or merging, if this looks good).

Copy link
Member

@dgrisonnet dgrisonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some new thoughts, I will try to have an actual look at the code later this week

docs/customresourcestate-metrics.md Show resolved Hide resolved
@eugenepaniot
Copy link

We're interested in this feature and hope to see it in the next release soon!

Allow CRS configuration to take in path values for lists, denoted by
"*". This means it's possible to specify `[..., <list>, "*", foo, ...]`
in the configuration to dynamically generate multiple metrics that
reflect different states of `foo` in various list elements.
allow specifying dynamic valueFrom based on wildcard labelFromPaths

Signed-off-by: Pranshu Srivastava <rexagod@gmail.com>
Comment on lines +591 to +595
path: mustCompilePath(t, "spec", "order", "*", "value"),
want: []valuePath{
mustCompilePath(t, "spec", "order", "0", "value"),
mustCompilePath(t, "spec", "order", "1", "value"),
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would happen if value was an optional field and wasn't present in one of the order?

Comment on lines +292 to +310
// search space to resolve the dynamic valueFrom from the wildcard labels
dynamicValueFromScope := c.compiledCommon.labelFromPath
lastUnderscoreIndex := strings.LastIndex(extractedValueFrom, "_")
if lastUnderscoreIndex != -1 {
// For:
// labelsFromPath:
// "foo_*": ["spec", "fooObj"]
unresolvedKey := extractedValueFrom[:lastUnderscoreIndex] + "_*"
dynamicPaths, ok := dynamicValueFromScope[unresolvedKey]
if ok {
var resolvedKeyArr []string
for _, dynamicPath := range dynamicPaths {
resolvedKeyArr = append(resolvedKeyArr, dynamicPath.part)
}
// resolvedKey will map to unresolved key "foo_*"'s corresponding valid path string, i.e., "spec_fooObj_*".
resolvedKey := strings.Join(resolvedKeyArr, "_")
extractedValueFrom = resolvedKey + extractedValueFrom[lastUnderscoreIndex:]
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we do that?

As far as I can tell this is made to propagate the wildcard to the value no? Like:

"foo_*": ["spec", "fooObj"]

yields the following path:

spec_fooObj_*

no?

Comment on lines +639 to +658
handleNil := func(object interface{}, part string) interface{} {
switch tobj := object.(type) {
case map[string]interface{}:
return tobj[part]
case []interface{}:
if part == "*" {
return tobj
}
idx, err := strconv.Atoi(part)
if err != nil {
return nil
}
if idx < 0 || idx >= len(tobj) {
return nil
}
return tobj[idx]
default:
return nil
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you make that its own function so that the code is easier to read?

@@ -674,14 +780,85 @@ func famGen(f compiledFamily) generator.FamilyGenerator {
}
}

func findWildcard(path valuePath, i *int) bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a fan of modifying the index and returning whether we find the wildcard or not. A more idiomatic approach would be similar to the search functions in the strings package where if found you return the index, otherwise -1

Comment on lines +819 to +827
pathCopyStart := make(valuePath, i)
copy(pathCopyStart, path[:i])
pathCopyWildcard := make(valuePath, len(path)-i-1)
copy(pathCopyWildcard, path[i+1:])
for k := 0; k < l; k++ {
pathCopyStart = append(pathCopyStart, pathOp{part: strconv.Itoa(k)})
pathCopyStart = append(pathCopyStart, pathCopyWildcard...)
expandedPaths = append(expandedPaths, pathCopyStart)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we know the size of the path from the get go, it would be less expensive in terms of allocations to just allocate one slice and then fill it correctly

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 4, 2024
@k8s-ci-robot
Copy link
Contributor

PR needs rebase.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. triage/accepted Indicates an issue or PR is ready to be actively worked on.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants