Skip to content
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
4 changes: 2 additions & 2 deletions internal/store/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package store
import (
"fmt"
"regexp"
"sort"
"slices"
"strconv"
"strings"

Expand Down Expand Up @@ -87,7 +87,7 @@ func mapToPrometheusLabels(labels map[string]string, prefix string) ([]string, [
for key := range labels {
sortedKeys = append(sortedKeys, key)
}
sort.Strings(sortedKeys)
slices.Sort(sortedKeys)

// conflictDesc holds some metadata for resolving potential label conflicts
type conflictDesc struct {
Expand Down
22 changes: 11 additions & 11 deletions pkg/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
} else {
configFile, err := os.ReadFile(filepath.Clean(got))
if err != nil {
return fmt.Errorf("failed to read opts config file: %v", err)
return fmt.Errorf("failed to read opts config file: %w", err)
}
// NOTE: Config value will override default values of intersecting options.
err = yaml.Unmarshal(configFile, opts)
Expand Down Expand Up @@ -184,7 +184,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {

kubeConfig, err := clientcmd.BuildConfigFromFlags(opts.Apiserver, opts.Kubeconfig)
if err != nil {
return fmt.Errorf("failed to build config from flags: %v", err)
return fmt.Errorf("failed to build config from flags: %w", err)
}

// Loading custom resource state configuration from cli argument or config file
Expand All @@ -199,7 +199,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
} else {
crcFile, err := os.ReadFile(filepath.Clean(opts.CustomResourceConfigFile))
if err != nil {
return fmt.Errorf("failed to read custom resource config file: %v", err)
return fmt.Errorf("failed to read custom resource config file: %w", err)
}
configSuccess.WithLabelValues("customresourceconfig", filepath.Clean(opts.CustomResourceConfigFile)).Set(1)
configSuccessTime.WithLabelValues("customresourceconfig", filepath.Clean(opts.CustomResourceConfigFile)).SetToCurrentTime()
Expand All @@ -223,7 +223,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
}

if err := storeBuilder.WithEnabledResources(resources); err != nil {
return fmt.Errorf("failed to set up resources: %v", err)
return fmt.Errorf("failed to set up resources: %w", err)
}

namespaces := opts.Namespaces.GetNamespaces()
Expand All @@ -249,14 +249,14 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {

err = allowDenyList.Parse()
if err != nil {
return fmt.Errorf("error initializing the allowdeny list: %v", err)
return fmt.Errorf("error initializing the allowdeny list: %w", err)
}

klog.InfoS("Metric allow-denylisting", "allowDenyStatus", allowDenyList.Status())

optInMetricFamilyFilter, err := optin.NewMetricFamilyFilter(opts.MetricOptInList)
if err != nil {
return fmt.Errorf("error initializing the opt-in metric list: %v", err)
return fmt.Errorf("error initializing the opt-in metric list: %w", err)
}

if optInMetricFamilyFilter.Count() > 0 {
Expand All @@ -276,18 +276,18 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
storeBuilder.WithUtilOptions(opts)
kubeClient, err := util.CreateKubeClient(opts.Apiserver, opts.Kubeconfig)
if err != nil {
return fmt.Errorf("failed to create client: %v", err)
return fmt.Errorf("failed to create client: %w", err)
}
storeBuilder.WithKubeClient(kubeClient)

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)
return fmt.Errorf("failed to set up annotations allowlist: %w", err)
}
klog.InfoS("Using annotations allowlist", "annotationsAllowList", opts.AnnotationsAllowList)

if err := storeBuilder.WithAllowLabels(opts.LabelsAllowList); err != nil {
return fmt.Errorf("failed to set up labels allowlist: %v", err)
return fmt.Errorf("failed to set up labels allowlist: %w", err)
}
klog.InfoS("Using labels allowlist", "labelsAllowList", opts.LabelsAllowList)

Expand Down Expand Up @@ -396,7 +396,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
}

if err := g.Run(); err != nil {
return fmt.Errorf("run server group error: %v", err)
return fmt.Errorf("run server group error: %w", err)
}

klog.InfoS("Exited")
Expand Down Expand Up @@ -628,7 +628,7 @@ func resolveCustomResourceConfig(opts *options.Options) (customresourcestate.Con
} else {
f, err := os.Open(filepath.Clean(file))
if err != nil {
return nil, fmt.Errorf("unable to open Custom Resource State Metrics file: %v", err)
return nil, fmt.Errorf("unable to open Custom Resource State Metrics file: %w", err)
}
return yaml.NewDecoder(f), nil
}
Expand Down
42 changes: 19 additions & 23 deletions pkg/customresourcestate/registry_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ package customresourcestate
import (
"errors"
"fmt"
"math"
"sort"
"slices"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -220,7 +219,7 @@ type compiledGauge struct {

func (c *compiledGauge) Values(v interface{}) (result []eachValue, errs []error) {
onError := func(err error) {
errs = append(errs, fmt.Errorf("%s: %v", c.Path(), err))
errs = append(errs, fmt.Errorf("%s: %w", c.Path(), err))
}
Comment on lines 220 to 223
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

PR description mentions switching fmt.Errorf("%v", err) to "%w" to preserve error chains. This file updates compiledGauge.Values to wrap with %w, but compiledInfo.Values still formats aggregated errors with %v (and therefore doesn’t preserve unwrap-able causes). Either update compiledInfo.Values similarly (e.g., by joining/wrapping underlying errors) or adjust the PR description/scope so it doesn’t imply full conversion.

Copilot uses AI. Check for mistakes.

switch iter := v.(type) {
Expand Down Expand Up @@ -312,7 +311,7 @@ type compiledInfo struct {

func (c *compiledInfo) Values(v interface{}) (result []eachValue, errs []error) {
onError := func(err ...error) {
errs = append(errs, fmt.Errorf("%s: %v", c.Path(), err))
errs = append(errs, fmt.Errorf("%s: %w", c.Path(), errors.Join(err...)))
}

switch iter := v.(type) {
Expand Down Expand Up @@ -418,30 +417,27 @@ func (c *compiledStateSet) values(v interface{}) (result []eachValue, errs []err
return
}

// less compares two maps of labels by keys and values
func less(a, b map[string]string) bool {
var aKeys, bKeys sort.StringSlice
// compareLabels compares two maps of labels by keys and values, returning
// a negative, zero, or positive int for use with slices.SortFunc.
func compareLabels(a, b map[string]string) int {
var aKeys, bKeys []string
for k := range a {
aKeys = append(aKeys, k)
}
for k := range b {
bKeys = append(bKeys, k)
}
aKeys.Sort()
bKeys.Sort()
for i := 0; i < int(math.Min(float64(len(aKeys)), float64(len(bKeys)))); i++ {
if aKeys[i] != bKeys[i] {
return aKeys[i] < bKeys[i]
slices.Sort(aKeys)
slices.Sort(bKeys)
for i := 0; i < min(len(aKeys), len(bKeys)); i++ {
if c := strings.Compare(aKeys[i], bKeys[i]); c != 0 {
return c
}

va := a[aKeys[i]]
vb := b[bKeys[i]]
if va == vb {
continue
if c := strings.Compare(a[aKeys[i]], b[bKeys[i]]); c != 0 {
return c
}
return va < vb
}
return len(aKeys) < len(bKeys)
return len(aKeys) - len(bKeys)
}

func (c compiledGauge) value(it interface{}) (*eachValue, error) {
Expand Down Expand Up @@ -486,7 +482,7 @@ func (e eachValue) ToMetric() *metric.Metric {
keys = append(keys, k)
}
// make it deterministic
sort.Strings(keys)
slices.Sort(keys)
for _, key := range keys {
values = append(values, e.Labels[key])
}
Expand Down Expand Up @@ -524,7 +520,7 @@ func addPathLabels(obj interface{}, labels map[string]valuePath, result map[stri
stars = append(stars, k)
}
}
sort.Strings(stars)
slices.Sort(stars)
for _, star := range stars {
m := labels[star].Get(obj)
if kv, ok := m.(map[string]interface{}); ok {
Expand Down Expand Up @@ -704,8 +700,8 @@ func scrapeValuesFor(e compiledEach, obj map[string]interface{}) ([]eachValue, [
result, errs := e.Values(v)

// return results in a consistent order (simplifies testing)
sort.Slice(result, func(i, j int) bool {
return less(result[i].Labels, result[j].Labels)
slices.SortFunc(result, func(a, b eachValue) int {
return compareLabels(a.Labels, b.Labels)
})
return result, errs
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/customresourcestate/registry_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,13 @@ func Test_values(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
gotResult, gotErrors := scrapeValuesFor(tt.each, cr)
assert.Equal(t, tt.wantResult, gotResult)
assert.Equal(t, tt.wantErrors, gotErrors)
if tt.wantErrors == nil {
assert.Nil(t, gotErrors)
} else if assert.Len(t, gotErrors, len(tt.wantErrors)) {
for i, wantErr := range tt.wantErrors {
assert.EqualError(t, gotErrors[i], wantErr.Error())
}
}
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/metrics_store/metrics_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (m MetricsWriter) WriteAll(w io.Writer) error {
m.stores[0].metrics.Range(func(_ interface{}, _ interface{}) bool {
_, err = w.Write([]byte(help))
if err != nil {
err = fmt.Errorf("failed to write help text: %v", err)
err = fmt.Errorf("failed to write help text: %w", err)
}
return false
})
Expand All @@ -80,7 +80,7 @@ func (m MetricsWriter) WriteAll(w io.Writer) error {
metricFamilies := value.([][]byte)
_, err = w.Write(metricFamilies[i])
if err != nil {
err = fmt.Errorf("failed to write metrics family: %v", err)
err = fmt.Errorf("failed to write metrics family: %w", err)
return false
}
return true
Expand Down
4 changes: 2 additions & 2 deletions pkg/optin/optin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package optin

import (
"regexp"
"sort"
"slices"
"strings"

generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator"
Expand Down Expand Up @@ -49,7 +49,7 @@ func (filter MetricFamilyFilter) Status() string {
asStrings = append(asStrings, metric.String())
}
// sort the strings for the sake of ux such that the resulting status is consistent
sort.Strings(asStrings)
slices.Sort(asStrings)
return strings.Join(asStrings, ", ")
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/options/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package options

import (
"errors"
"sort"
"slices"
"strings"

"k8s.io/apimachinery/pkg/fields"
Expand All @@ -36,7 +36,7 @@ type MetricSet map[string]struct{}
func (ms *MetricSet) String() string {
s := *ms
ss := s.asSlice()
sort.Strings(ss)
slices.Sort(ss)
return strings.Join(ss, ",")
}

Expand Down Expand Up @@ -73,7 +73,7 @@ type ResourceSet map[string]struct{}
func (r *ResourceSet) String() string {
s := *r
ss := s.AsSlice()
sort.Strings(ss)
slices.Sort(ss)
return strings.Join(ss, ",")
}

Expand Down Expand Up @@ -307,7 +307,7 @@ func (l LabelsAllowList) asSlice() []string {
func (l *LabelsAllowList) String() string {
s := *l
ss := s.asSlice()
sort.Strings(ss)
slices.Sort(ss)
return strings.Join(ss, ",")
}

Expand Down