Skip to content

Commit

Permalink
Allow optional VK in CR metrics
Browse files Browse the repository at this point in the history
Allow optional VK in CR metrics, while only requiring the group to be
fixed. This would allow users to define custom metrics for:
* a specific API version: version is fixed, kind varies.
* all the API versions of a resource: version varies, kind is fixed.
* all the APIs part of an API group: version varies, kind varies.

Signed-off-by: Pranshu Srivastava <rexagod@gmail.com>
  • Loading branch information
rexagod committed Oct 10, 2022
1 parent 12402a5 commit 7bc684a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 2 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func main() {

var factories []customresource.RegistryFactory
if config, set := resolveCustomResourceConfig(opts); set {
crf, err := customresourcestate.FromConfig(config)
crf, err := customresourcestate.FromConfig(config, opts)
if err != nil {
klog.ErrorS(err, "Parsing from Custom Resource State Metrics file failed")
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
Expand Down
81 changes: 80 additions & 1 deletion pkg/customresourcestate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ import (
"strings"

"github.com/gobuffalo/flect"
"k8s.io/client-go/discovery"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"

"k8s.io/kube-state-metrics/v2/pkg/customresource"
"k8s.io/kube-state-metrics/v2/pkg/options"
)

// Metrics is the top level configuration object.
Expand Down Expand Up @@ -161,14 +164,90 @@ type ConfigDecoder interface {
Decode(v interface{}) (err error)
}

// generateResources generates the set of possible resources from the varying V and, or K of the original config.
func generateResources(resources []Resource, opts *options.Options) []Resource {
d := getDiscoveryClient(opts)
_, discoveredResources, err := d.ServerGroupsAndResources()
if err != nil {
klog.Fatalf("Failed to get resource lists: %v", err)
}
r := map[GroupVersionKind]bool{}
var out []Resource
for resourceIndex := range resources {
configGVK := resources[resourceIndex].GroupVersionKind
originalConfigGVK := configGVK
if configGVK.Version == "" || configGVK.Kind == "" {
// discoveredResources is a list of APIGroupResources, which is a list of APIResources.
// These use the GV as the primary key.
for groupVersionListIndex := range discoveredResources {
var discoveredGroup string
if strings.Contains(discoveredResources[groupVersionListIndex].GroupVersion, "/") {
discoveredGroup = strings.Split(discoveredResources[groupVersionListIndex].GroupVersion, "/")[0]
} else {
discoveredGroup = ""
}
if discoveredGroup == configGVK.Group {
for discoveredResourceIndex := range discoveredResources[groupVersionListIndex].APIResources {
discoveredResource := discoveredResources[groupVersionListIndex].APIResources[discoveredResourceIndex]
if discoveredResource.Group != "" {
continue
}
// - groupVersionKind:
// group: "apps"
if configGVK.Version == "" && configGVK.Kind == "" {
configGVK.Version = discoveredResource.Version
configGVK.Kind = discoveredResource.Kind
}
// - groupVersionKind:
// group: "apps"
// kind: "Deployment"
if configGVK.Version == "" && configGVK.Kind != "" {
if discoveredResource.Kind != "" {
continue
}
configGVK.Version = discoveredResource.Version
}
// - groupVersionKind:
// group: "apps"
// version: "v1"
if configGVK.Version != "" && configGVK.Kind == "" {
if discoveredResource.Version != "" {
continue
}
configGVK.Kind = discoveredResource.Kind
}
// Collect all possible GVKs for the current config.
r[configGVK] = true
configGVK = originalConfigGVK
}
}
}
}
for gvk := range r {
resources[resourceIndex].GroupVersionKind = gvk
out = append(out, resources[resourceIndex])
}
}
return out
}

func getDiscoveryClient(opts *options.Options) *discovery.DiscoveryClient {
config, err := clientcmd.BuildConfigFromFlags(opts.Apiserver, opts.Kubeconfig)
if err != nil {
klog.Fatalf("Error building kubeconfig: %v", err)
}
return discovery.NewDiscoveryClientForConfigOrDie(config)
}

// FromConfig decodes a configuration source into a slice of customresource.RegistryFactory that are ready to use.
func FromConfig(decoder ConfigDecoder) ([]customresource.RegistryFactory, error) {
func FromConfig(decoder ConfigDecoder, opts *options.Options) ([]customresource.RegistryFactory, error) {
var crconfig Metrics
var factories []customresource.RegistryFactory
factoriesIndex := map[string]bool{}
if err := decoder.Decode(&crconfig); err != nil {
return nil, fmt.Errorf("failed to parse Custom Resource State metrics: %w", err)
}
crconfig.Spec.Resources = generateResources(crconfig.Spec.Resources, opts)
for _, resource := range crconfig.Spec.Resources {
factory, err := NewCustomResourceMetrics(resource)
if err != nil {
Expand Down

0 comments on commit 7bc684a

Please sign in to comment.