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: Use PartialObjectMetadata for Configmaps #2468

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions internal/store/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ import (
policyv1 "k8s.io/api/policy/v1"
rbacv1 "k8s.io/api/rbac/v1"
storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/metadata"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"

Expand Down Expand Up @@ -66,12 +68,14 @@ var _ ksmtypes.BuilderInterface = &Builder{}
// (https://en.wikipedia.org/wiki/Builder_pattern).
type Builder struct {
kubeClient clientset.Interface
metadataOnlyKubeClient metadata.Interface
customResourceClients map[string]interface{}
ctx context.Context
familyGeneratorFilter generator.FamilyGeneratorFilter
customResourceClients map[string]interface{}
listWatchMetrics *watch.ListWatchMetrics
shardingMetrics *sharding.Metrics
buildStoresFunc ksmtypes.BuildStoresFunc
buildMetadataOnlyStoresFunc ksmtypes.BuildMetadataOnlyStoresFunc
buildCustomResourceStoresFunc ksmtypes.BuildCustomResourceStoresFunc
allowAnnotationsList map[string][]string
allowLabelsList map[string][]string
Expand Down Expand Up @@ -155,6 +159,11 @@ func (b *Builder) WithKubeClient(c clientset.Interface) {
b.kubeClient = c
}

// WithMetadataOnlyKubeClient sets the metadataOnlyKubeClient property of a Builder.
func (b *Builder) WithMetadataOnlyKubeClient(c metadata.Interface) {
b.metadataOnlyKubeClient = c
}

// WithCustomResourceClients sets the customResourceClients property of a Builder.
func (b *Builder) WithCustomResourceClients(cs map[string]interface{}) {
b.customResourceClients = cs
Expand All @@ -176,6 +185,11 @@ func (b *Builder) WithGenerateStoresFunc(f ksmtypes.BuildStoresFunc) {
b.buildStoresFunc = f
}

// WithGenerateMetadataOnlyStoresFunc configures a custom generate custom resource store function
func (b *Builder) WithGenerateMetadataOnlyStoresFunc(f ksmtypes.BuildMetadataOnlyStoresFunc) {
b.buildMetadataOnlyStoresFunc = f
}

// WithGenerateCustomResourceStoresFunc configures a custom generate custom resource store function
func (b *Builder) WithGenerateCustomResourceStoresFunc(f ksmtypes.BuildCustomResourceStoresFunc) {
b.buildCustomResourceStoresFunc = f
Expand All @@ -186,6 +200,11 @@ func (b *Builder) DefaultGenerateStoresFunc() ksmtypes.BuildStoresFunc {
return b.buildStores
}

// DefaultGenerateMetadataOnlyStoresFunc returns default buildStores function
func (b *Builder) DefaultGenerateMetadataOnlyStoresFunc() ksmtypes.BuildMetadataOnlyStoresFunc {
return b.buildMetadataOnlyStores
}

// DefaultGenerateCustomResourceStoresFunc returns default buildCustomResourceStores function
func (b *Builder) DefaultGenerateCustomResourceStoresFunc() ksmtypes.BuildCustomResourceStoresFunc {
return b.buildCustomResourceStores
Expand Down Expand Up @@ -360,7 +379,7 @@ func availableResources() []string {
}

func (b *Builder) buildConfigMapStores() []cache.Store {
return b.buildStoresFunc(configMapMetricFamilies(b.allowAnnotationsList["configmaps"], b.allowLabelsList["configmaps"]), &v1.ConfigMap{}, createConfigMapListWatch, b.useAPIServerCache)
return b.buildMetadataOnlyStoresFunc(configMapMetricFamilies(b.allowAnnotationsList["configmaps"], b.allowLabelsList["configmaps"]), &metav1.PartialObjectMetadata{}, createConfigMapListWatch, b.useAPIServerCache)
}

func (b *Builder) buildCronJobStores() []cache.Store {
Expand Down Expand Up @@ -539,6 +558,46 @@ func (b *Builder) buildStores(
return stores
}

func (b *Builder) buildMetadataOnlyStores(
Copy link
Member

Choose a reason for hiding this comment

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

I might be missing something, but it seems you could call buildStores right away since this is an exact copy of its code.

Going even further, maybe we don't even need buildMetadataOnlyStores and could just call buildStores directly when building resource specific stores like:

-	return b.buildStoresFunc(configMapMetricFamilies(b.allowAnnotationsList["configmaps"], b.allowLabelsList["configmaps"]), &v1.ConfigMap{}, createConfigMapListWatch, b.useAPIServerCache)
+	return b.buildStoresFunc(configMapMetricFamilies(b.allowAnnotationsList["configmaps"], b.allowLabelsList["configmaps"]), &metav1.PartialObjectMetadata{}, createConfigMapListWatch, b.useAPIServerCache)

Copy link
Member Author

Choose a reason for hiding this comment

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

The reason I created that is the different signature of the listWatchFunc (using metadata.Interface not clientset.Interface). Is there's a different way to do that, I'm all ears :)

metricFamilies []generator.FamilyGenerator,
expectedType interface{},
listWatchFunc func(kubeClient metadata.Interface, ns string, fieldSelector string) cache.ListerWatcher,
useAPIServerCache bool,
) []cache.Store {
metricFamilies = generator.FilterFamilyGenerators(b.familyGeneratorFilter, metricFamilies)
composedMetricGenFuncs := generator.ComposeMetricGenFuncs(metricFamilies)
familyHeaders := generator.ExtractMetricFamilyHeaders(metricFamilies)

if b.namespaces.IsAllNamespaces() {
store := metricsstore.NewMetricsStore(
familyHeaders,
composedMetricGenFuncs,
)
if b.fieldSelectorFilter != "" {
klog.InfoS("FieldSelector is used", "fieldSelector", b.fieldSelectorFilter)
}
listWatcher := listWatchFunc(b.metadataOnlyKubeClient, v1.NamespaceAll, b.fieldSelectorFilter)
b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
return []cache.Store{store}
}

stores := make([]cache.Store, 0, len(b.namespaces))
for _, ns := range b.namespaces {
store := metricsstore.NewMetricsStore(
familyHeaders,
composedMetricGenFuncs,
)
if b.fieldSelectorFilter != "" {
klog.InfoS("FieldSelector is used", "fieldSelector", b.fieldSelectorFilter)
}
listWatcher := listWatchFunc(b.metadataOnlyKubeClient, ns, b.fieldSelectorFilter)
b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
stores = append(stores, store)
}

return stores
}

// TODO(Garrybest): Merge `buildStores` and `buildCustomResourceStores`
func (b *Builder) buildCustomResourceStores(resourceName string,
metricFamilies []generator.FamilyGenerator,
Expand Down
24 changes: 12 additions & 12 deletions internal/store/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ package store
import (
"context"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/metadata"
"k8s.io/client-go/tools/cache"
basemetrics "k8s.io/component-base/metrics"

Expand All @@ -43,7 +43,7 @@ func configMapMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
metric.Gauge,
basemetrics.ALPHA,
"",
wrapConfigMapFunc(func(c *v1.ConfigMap) *metric.Family {
wrapConfigMapFunc(func(c *metav1.PartialObjectMetadata) *metric.Family {
if len(allowAnnotationsList) == 0 {
return &metric.Family{}
}
Expand All @@ -65,7 +65,7 @@ func configMapMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
metric.Gauge,
basemetrics.STABLE,
"",
wrapConfigMapFunc(func(c *v1.ConfigMap) *metric.Family {
wrapConfigMapFunc(func(c *metav1.PartialObjectMetadata) *metric.Family {
if len(allowLabelsList) == 0 {
return &metric.Family{}
}
Expand All @@ -87,7 +87,7 @@ func configMapMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
metric.Gauge,
basemetrics.STABLE,
"",
wrapConfigMapFunc(func(_ *v1.ConfigMap) *metric.Family {
wrapConfigMapFunc(func(_ *metav1.PartialObjectMetadata) *metric.Family {
return &metric.Family{
Metrics: []*metric.Metric{{
LabelKeys: []string{},
Expand All @@ -103,7 +103,7 @@ func configMapMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
metric.Gauge,
basemetrics.STABLE,
"",
wrapConfigMapFunc(func(c *v1.ConfigMap) *metric.Family {
wrapConfigMapFunc(func(c *metav1.PartialObjectMetadata) *metric.Family {
ms := []*metric.Metric{}

if !c.CreationTimestamp.IsZero() {
Expand All @@ -125,7 +125,7 @@ func configMapMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
metric.Gauge,
basemetrics.ALPHA,
"",
wrapConfigMapFunc(func(c *v1.ConfigMap) *metric.Family {
wrapConfigMapFunc(func(c *metav1.PartialObjectMetadata) *metric.Family {
return &metric.Family{
Metrics: resourceVersionMetric(c.ObjectMeta.ResourceVersion),
}
Expand All @@ -134,22 +134,22 @@ func configMapMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
}
}

func createConfigMapListWatch(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher {
func createConfigMapListWatch(kubeClient metadata.Interface, ns string, fieldSelector string) cache.ListerWatcher {
return &cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
opts.FieldSelector = fieldSelector
return kubeClient.CoreV1().ConfigMaps(ns).List(context.TODO(), opts)
return kubeClient.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}).Namespace(ns).List(context.TODO(), opts)
},
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
opts.FieldSelector = fieldSelector
return kubeClient.CoreV1().ConfigMaps(ns).Watch(context.TODO(), opts)
return kubeClient.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}).Namespace(ns).Watch(context.TODO(), opts)
},
}
}

func wrapConfigMapFunc(f func(*v1.ConfigMap) *metric.Family) func(interface{}) *metric.Family {
func wrapConfigMapFunc(f func(*metav1.PartialObjectMetadata) *metric.Family) func(interface{}) *metric.Family {
return func(obj interface{}) *metric.Family {
configMap := obj.(*v1.ConfigMap)
configMap := obj.(*metav1.PartialObjectMetadata)

metricFamily := f(configMap)

Expand Down
5 changes: 2 additions & 3 deletions internal/store/configmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package store
import (
"testing"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator"
Expand All @@ -37,7 +36,7 @@ func TestConfigMapStore(t *testing.T) {
AllowLabelsList: []string{
"app",
},
Obj: &v1.ConfigMap{
Obj: &metav1.PartialObjectMetadata{
ObjectMeta: metav1.ObjectMeta{
Name: "configmap1",
Namespace: "ns1",
Expand Down Expand Up @@ -73,7 +72,7 @@ func TestConfigMapStore(t *testing.T) {
},
},
{
Obj: &v1.ConfigMap{
Obj: &metav1.PartialObjectMetadata{
ObjectMeta: metav1.ObjectMeta{
Name: "configmap2",
Namespace: "ns2",
Expand Down
7 changes: 7 additions & 0 deletions pkg/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {

storeBuilder.WithUsingAPIServerCache(opts.UseAPIServerCache)
storeBuilder.WithGenerateStoresFunc(storeBuilder.DefaultGenerateStoresFunc())
storeBuilder.WithGenerateMetadataOnlyStoresFunc(storeBuilder.DefaultGenerateMetadataOnlyStoresFunc())
proc.StartReaper()

storeBuilder.WithUtilOptions(opts)
Expand All @@ -264,6 +265,12 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
}
storeBuilder.WithKubeClient(kubeClient)

metadataOnlyKubeClient, err := util.CreateMetadataOnlyKubeClient(opts.Apiserver, opts.Kubeconfig)
if err != nil {
return fmt.Errorf("failed to create metadata-only client: %v", err)
}
storeBuilder.WithMetadataOnlyKubeClient(metadataOnlyKubeClient)

storeBuilder.WithSharding(opts.Shard, opts.TotalShards)
if err := storeBuilder.WithAllowAnnotations(opts.AnnotationsAllowList); err != nil {
return fmt.Errorf("failed to set up annotations allowlist: %v", err)
Expand Down
18 changes: 18 additions & 0 deletions pkg/app/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes/fake"
mfake "k8s.io/client-go/metadata/fake"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -67,6 +68,7 @@ func BenchmarkKubeStateMetrics(b *testing.B) {
)

kubeClient := fake.NewSimpleClientset()
metadataOnlyKubeClient := mfake.NewSimpleMetadataClient(mfake.NewTestScheme())

if err := injectFixtures(kubeClient, fixtureMultiplier); err != nil {
b.Errorf("error injecting resources: %v", err)
Expand All @@ -86,10 +88,12 @@ func BenchmarkKubeStateMetrics(b *testing.B) {
b.Fatal(err)
}
builder.WithKubeClient(kubeClient)
builder.WithMetadataOnlyKubeClient(metadataOnlyKubeClient)
builder.WithSharding(0, 1)
builder.WithContext(ctx)
builder.WithNamespaces(options.DefaultNamespaces)
builder.WithGenerateStoresFunc(builder.DefaultGenerateStoresFunc())
builder.WithGenerateMetadataOnlyStoresFunc(builder.DefaultGenerateMetadataOnlyStoresFunc())

allowDenyListFilter, err := allowdenylist.New(map[string]struct{}{}, map[string]struct{}{})
if err != nil {
Expand Down Expand Up @@ -147,6 +151,7 @@ func TestFullScrapeCycle(t *testing.T) {
t.Parallel()

kubeClient := fake.NewSimpleClientset()
metadataOnlyKubeClient := mfake.NewSimpleMetadataClient(mfake.NewTestScheme())

err := pod(kubeClient, 0)
if err != nil {
Expand All @@ -163,8 +168,10 @@ func TestFullScrapeCycle(t *testing.T) {
t.Fatal(err)
}
builder.WithKubeClient(kubeClient)
builder.WithMetadataOnlyKubeClient(metadataOnlyKubeClient)
builder.WithNamespaces(options.DefaultNamespaces)
builder.WithGenerateStoresFunc(builder.DefaultGenerateStoresFunc())
builder.WithGenerateMetadataOnlyStoresFunc(builder.DefaultGenerateMetadataOnlyStoresFunc())

l, err := allowdenylist.New(map[string]struct{}{}, map[string]struct{}{})
if err != nil {
Expand Down Expand Up @@ -442,6 +449,7 @@ func TestShardingEquivalenceScrapeCycle(t *testing.T) {
t.Parallel()

kubeClient := fake.NewSimpleClientset()
metadataOnlyKubeClient := mfake.NewSimpleMetadataClient(mfake.NewTestScheme())

for i := 0; i < 10; i++ {
err := pod(kubeClient, i)
Expand All @@ -465,10 +473,12 @@ func TestShardingEquivalenceScrapeCycle(t *testing.T) {
t.Fatal(err)
}
unshardedBuilder.WithKubeClient(kubeClient)
unshardedBuilder.WithMetadataOnlyKubeClient(metadataOnlyKubeClient)
unshardedBuilder.WithNamespaces(options.DefaultNamespaces)
unshardedBuilder.WithFamilyGeneratorFilter(l)
unshardedBuilder.WithAllowLabels(map[string][]string{})
unshardedBuilder.WithGenerateStoresFunc(unshardedBuilder.DefaultGenerateStoresFunc())
unshardedBuilder.WithGenerateMetadataOnlyStoresFunc(unshardedBuilder.DefaultGenerateMetadataOnlyStoresFunc())

unshardedHandler := metricshandler.New(&options.Options{}, kubeClient, unshardedBuilder, false)
unshardedHandler.ConfigureSharding(ctx, 0, 1)
Expand All @@ -481,10 +491,13 @@ func TestShardingEquivalenceScrapeCycle(t *testing.T) {
t.Fatal(err)
}
shardedBuilder1.WithKubeClient(kubeClient)
shardedBuilder1.WithMetadataOnlyKubeClient(metadataOnlyKubeClient)

shardedBuilder1.WithNamespaces(options.DefaultNamespaces)
shardedBuilder1.WithFamilyGeneratorFilter(l)
shardedBuilder1.WithAllowLabels(map[string][]string{})
shardedBuilder1.WithGenerateStoresFunc(shardedBuilder1.DefaultGenerateStoresFunc())
shardedBuilder1.WithGenerateMetadataOnlyStoresFunc(shardedBuilder1.DefaultGenerateMetadataOnlyStoresFunc())

shardedHandler1 := metricshandler.New(&options.Options{}, kubeClient, shardedBuilder1, false)
shardedHandler1.ConfigureSharding(ctx, 0, 2)
Expand All @@ -497,10 +510,12 @@ func TestShardingEquivalenceScrapeCycle(t *testing.T) {
t.Fatal(err)
}
shardedBuilder2.WithKubeClient(kubeClient)
shardedBuilder2.WithMetadataOnlyKubeClient(metadataOnlyKubeClient)
shardedBuilder2.WithNamespaces(options.DefaultNamespaces)
shardedBuilder2.WithFamilyGeneratorFilter(l)
shardedBuilder2.WithAllowLabels(map[string][]string{})
shardedBuilder2.WithGenerateStoresFunc(shardedBuilder2.DefaultGenerateStoresFunc())
shardedBuilder2.WithGenerateMetadataOnlyStoresFunc(shardedBuilder2.DefaultGenerateMetadataOnlyStoresFunc())

shardedHandler2 := metricshandler.New(&options.Options{}, kubeClient, shardedBuilder2, false)
shardedHandler2.ConfigureSharding(ctx, 1, 2)
Expand Down Expand Up @@ -612,6 +627,7 @@ func TestShardingEquivalenceScrapeCycle(t *testing.T) {
// We use custom resource object samplev1alpha1.Foo in kubernetes/sample-controller as an example.
func TestCustomResourceExtension(t *testing.T) {
kubeClient := fake.NewSimpleClientset()
metadataOnlyKubeClient := mfake.NewSimpleMetadataClient(mfake.NewTestScheme())
factories := []customresource.RegistryFactory{new(fooFactory)}
resources := options.DefaultResources.AsSlice()
customResourceClients := make(map[string]interface{}, len(factories))
Expand All @@ -638,9 +654,11 @@ func TestCustomResourceExtension(t *testing.T) {
}

builder.WithKubeClient(kubeClient)
builder.WithMetadataOnlyKubeClient(metadataOnlyKubeClient)
builder.WithCustomResourceClients(customResourceClients)
builder.WithNamespaces(options.DefaultNamespaces)
builder.WithGenerateStoresFunc(builder.DefaultGenerateStoresFunc())
builder.WithGenerateMetadataOnlyStoresFunc(builder.DefaultGenerateMetadataOnlyStoresFunc())
builder.WithGenerateCustomResourceStoresFunc(builder.DefaultGenerateCustomResourceStoresFunc())

l, err := allowdenylist.New(map[string]struct{}{}, map[string]struct{}{})
Expand Down
Loading