diff --git a/internal/store/utils.go b/internal/store/utils.go index dce6730d0f..f18b544e43 100644 --- a/internal/store/utils.go +++ b/internal/store/utils.go @@ -19,7 +19,7 @@ package store import ( "fmt" "regexp" - "sort" + "slices" "strconv" "strings" @@ -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 { diff --git a/pkg/app/server.go b/pkg/app/server.go index 72518d5c94..b9cf0767e8 100644 --- a/pkg/app/server.go +++ b/pkg/app/server.go @@ -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) @@ -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 @@ -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() @@ -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() @@ -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 { @@ -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) @@ -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") @@ -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 } diff --git a/pkg/customresourcestate/registry_factory.go b/pkg/customresourcestate/registry_factory.go index 2a2cb067fc..bc50a57270 100644 --- a/pkg/customresourcestate/registry_factory.go +++ b/pkg/customresourcestate/registry_factory.go @@ -19,8 +19,7 @@ package customresourcestate import ( "errors" "fmt" - "math" - "sort" + "slices" "strconv" "strings" "time" @@ -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)) } switch iter := v.(type) { @@ -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) { @@ -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) { @@ -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]) } @@ -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 { @@ -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 } diff --git a/pkg/customresourcestate/registry_factory_test.go b/pkg/customresourcestate/registry_factory_test.go index d8ba0783d7..1af84beeff 100644 --- a/pkg/customresourcestate/registry_factory_test.go +++ b/pkg/customresourcestate/registry_factory_test.go @@ -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()) + } + } }) } } diff --git a/pkg/metrics_store/metrics_writer.go b/pkg/metrics_store/metrics_writer.go index 46eea70516..4dfca8fc8b 100644 --- a/pkg/metrics_store/metrics_writer.go +++ b/pkg/metrics_store/metrics_writer.go @@ -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 }) @@ -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 diff --git a/pkg/optin/optin.go b/pkg/optin/optin.go index 7f57cbfbdb..69f0241045 100644 --- a/pkg/optin/optin.go +++ b/pkg/optin/optin.go @@ -18,7 +18,7 @@ package optin import ( "regexp" - "sort" + "slices" "strings" generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" @@ -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, ", ") } diff --git a/pkg/options/types.go b/pkg/options/types.go index 93f8a7f505..3682db1b9a 100644 --- a/pkg/options/types.go +++ b/pkg/options/types.go @@ -18,7 +18,7 @@ package options import ( "errors" - "sort" + "slices" "strings" "k8s.io/apimachinery/pkg/fields" @@ -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, ",") } @@ -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, ",") } @@ -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, ",") }