From a8159fd955c8a5130f9229b167a7394c71665559 Mon Sep 17 00:00:00 2001 From: Yuchen Wang <162491048+yuchen-db@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:14:53 -0800 Subject: [PATCH 1/9] Add Rendezvous hashing and ingestion routing improvements (#303) --- cmd/thanos/receive.go | 4 +- pkg/receive/aligned_hashring.go | 153 ------ pkg/receive/aligned_hashring_test.go | 378 --------------- pkg/receive/config.go | 11 +- pkg/receive/handler.go | 2 +- pkg/receive/handler_test.go | 2 +- pkg/receive/hashring.go | 352 +++++++------- pkg/receive/hashring_test.go | 503 +++++--------------- pkg/receive/rendezvous_hashring.go | 108 +++++ pkg/receive/rendezvous_hashring_test.go | 593 ++++++++++++++++++++++++ pkg/store/labelpb/label.go | 25 +- 11 files changed, 997 insertions(+), 1134 deletions(-) delete mode 100644 pkg/receive/aligned_hashring.go delete mode 100644 pkg/receive/aligned_hashring_test.go create mode 100644 pkg/receive/rendezvous_hashring.go create mode 100644 pkg/receive/rendezvous_hashring_test.go diff --git a/cmd/thanos/receive.go b/cmd/thanos/receive.go index 714612bba10..aebb8c72274 100644 --- a/cmd/thanos/receive.go +++ b/cmd/thanos/receive.go @@ -1096,10 +1096,10 @@ func (rc *receiveConfig) registerFlag(cmd extkingpin.FlagClause) { cmd.Flag("receive.hashrings", "Alternative to 'receive.hashrings-file' flag (lower priority). Content of file that contains the hashring configuration.").PlaceHolder("").StringVar(&rc.hashringsFileContent) - hashringAlgorithmsHelptext := strings.Join([]string{string(receive.AlgorithmHashmod), string(receive.AlgorithmKetama)}, ", ") + hashringAlgorithmsHelptext := strings.Join([]string{string(receive.AlgorithmHashmod), string(receive.AlgorithmKetama), string(receive.AlgorithmRendezvous)}, ", ") cmd.Flag("receive.hashrings-algorithm", "The algorithm used when distributing series in the hashrings. Must be one of "+hashringAlgorithmsHelptext+". Will be overwritten by the tenant-specific algorithm in the hashring config."). Default(string(receive.AlgorithmHashmod)). - EnumVar(&rc.hashringsAlgorithm, string(receive.AlgorithmHashmod), string(receive.AlgorithmKetama), string(receive.AlgorithmAlignedKetama)) + EnumVar(&rc.hashringsAlgorithm, string(receive.AlgorithmHashmod), string(receive.AlgorithmKetama), string(receive.AlgorithmRendezvous)) rc.refreshInterval = extkingpin.ModelDuration(cmd.Flag("receive.hashrings-file-refresh-interval", "Refresh interval to re-read the hashring configuration file. (used as a fallback)"). Default("5m")) diff --git a/pkg/receive/aligned_hashring.go b/pkg/receive/aligned_hashring.go deleted file mode 100644 index 30203fbb90d..00000000000 --- a/pkg/receive/aligned_hashring.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) The Thanos Authors. -// Licensed under the Apache License 2.0. - -package receive - -import ( - "fmt" - "sort" - "strconv" - - "github.com/cespare/xxhash" - "github.com/pkg/errors" - - "github.com/thanos-io/thanos/pkg/strutil" -) - -// groupByAZ groups endpoints by Availability Zone and sorts them by their inferred ordinal in a k8s statefulset. -// It returns a 2D slice where each inner slice represents an AZ (sorted alphabetically) -// and contains endpoints sorted by ordinal. All inner slices are truncated to the -// length of the largest common sequence of ordinals starting from 0 across all AZs. -// All endpoint addresses must be valid k8s DNS names with 0-index ordinals at the end of the pod name. -func groupByAZ(endpoints []Endpoint) ([][]Endpoint, error) { - if len(endpoints) == 0 { - return nil, errors.New("no endpoints provided") - } - - // Group endpoints by AZ and then by ordinal. - azEndpoints := make(map[string]map[int]Endpoint) - for _, ep := range endpoints { - ordinal, err := strutil.ExtractPodOrdinal(ep.Address) - if err != nil { - return nil, errors.Wrapf(err, "failed to extract ordinal from address %s", ep.Address) - } - if _, ok := azEndpoints[ep.AZ]; !ok { - azEndpoints[ep.AZ] = make(map[int]Endpoint) - } - if _, exists := azEndpoints[ep.AZ][ordinal]; exists { - return nil, fmt.Errorf("duplicate endpoint ordinal %d for address %s in AZ %s", ordinal, ep.Address, ep.AZ) - } - azEndpoints[ep.AZ][ordinal] = ep - } - - // Get sorted list of AZ names. - sortedAZs := make([]string, 0, len(azEndpoints)) - for az := range azEndpoints { - sortedAZs = append(sortedAZs, az) - } - sort.Strings(sortedAZs) - - // Determine the maximum common ordinal across all AZs. - maxCommonOrdinal := -1 - for i := 0; ; i++ { - presentInAllAZs := true - for _, az := range sortedAZs { - if _, ok := azEndpoints[az][i]; !ok { - presentInAllAZs = false - if i == 0 { - return nil, fmt.Errorf("AZ %q is missing endpoint with ordinal 0", az) - } - break - } - } - if !presentInAllAZs { - maxCommonOrdinal = i - 1 - break - } - } - if maxCommonOrdinal < 0 { - return nil, errors.New("no common endpoints with ordinal 0 found across all AZs") - } - numAZs := len(sortedAZs) - result := make([][]Endpoint, numAZs) - for i, az := range sortedAZs { - result[i] = make([]Endpoint, 0, maxCommonOrdinal+1) - for j := 0; j <= maxCommonOrdinal; j++ { - result[i] = append(result[i], azEndpoints[az][j]) - } - } - return result, nil -} - -// newAlignedKetamaHashring creates a Ketama hash ring where replicas are strictly aligned across Availability Zones. -// Each section on the hash ring corresponds to a primary endpoint (taken from the first AZ) and its -// aligned replicas in other AZs (endpoints with the same ordinal). The hash for a section is calculated -// based *only* on the primary endpoint's address. -func newAlignedKetamaHashring(endpoints []Endpoint, sectionsPerNode int, replicationFactor uint64) (*ketamaHashring, error) { - if replicationFactor == 0 { - return nil, errors.New("replication factor cannot be zero") - } - if sectionsPerNode <= 0 { - return nil, errors.New("sections per node must be positive") - } - groupedEndpoints, err := groupByAZ(endpoints) - if err != nil { - return nil, errors.Wrap(err, "failed to group endpoints by AZ") - } - numAZs := len(groupedEndpoints) - if numAZs == 0 { - return nil, errors.New("no endpoint groups found after grouping by AZ") - } - if uint64(numAZs) != replicationFactor { - return nil, fmt.Errorf("number of AZs (%d) must equal replication factor (%d)", numAZs, replicationFactor) - } - numEndpointsPerAZ := len(groupedEndpoints[0]) - if numEndpointsPerAZ == 0 { - return nil, errors.New("AZ groups are empty after grouping") - } - totalEndpoints := numAZs * numEndpointsPerAZ - flatEndpoints := make([]Endpoint, 0, totalEndpoints) - for azIndex := 0; azIndex < numAZs; azIndex++ { - flatEndpoints = append(flatEndpoints, groupedEndpoints[azIndex]...) - } - hasher := xxhash.New() - ringSections := make(sections, 0, numEndpointsPerAZ*sectionsPerNode) - - // Iterate through primary endpoints (those in the first AZ) to define sections. - for primaryOrdinalIndex := 0; primaryOrdinalIndex < numEndpointsPerAZ; primaryOrdinalIndex++ { - primaryEndpoint := groupedEndpoints[0][primaryOrdinalIndex] - for sectionIndex := 1; sectionIndex <= sectionsPerNode; sectionIndex++ { - hasher.Reset() - _, _ = hasher.Write([]byte(primaryEndpoint.Address + ":" + strconv.Itoa(sectionIndex))) - sectionHash := hasher.Sum64() - sec := §ion{ - hash: sectionHash, - az: primaryEndpoint.AZ, - endpointIndex: uint64(primaryOrdinalIndex), - replicas: make([]uint64, 0, replicationFactor), - } - - // Find indices of all replicas (including primary) in the flat list and verify alignment. - for azIndex := 0; azIndex < numAZs; azIndex++ { - replicaFlatIndex := azIndex*numEndpointsPerAZ + primaryOrdinalIndex - replicaEndpoint := flatEndpoints[replicaFlatIndex] - replicaOrdinal, err := strutil.ExtractPodOrdinal(replicaEndpoint.Address) - if err != nil { - return nil, errors.Wrapf(err, "failed to extract ordinal from replica endpoint %s in AZ %s", replicaEndpoint.Address, replicaEndpoint.AZ) - } - if replicaOrdinal != primaryOrdinalIndex { - return nil, fmt.Errorf("ordinal mismatch for primary endpoint %s (ordinal %d): replica %s in AZ %s has ordinal %d", - primaryEndpoint.Address, primaryOrdinalIndex, replicaEndpoint.Address, replicaEndpoint.AZ, replicaOrdinal) - } - sec.replicas = append(sec.replicas, uint64(replicaFlatIndex)) - } - ringSections = append(ringSections, sec) - } - } - sort.Sort(ringSections) - return &ketamaHashring{ - endpoints: flatEndpoints, - sections: ringSections, - numEndpoints: uint64(totalEndpoints), - }, nil -} diff --git a/pkg/receive/aligned_hashring_test.go b/pkg/receive/aligned_hashring_test.go deleted file mode 100644 index c5e9feb611e..00000000000 --- a/pkg/receive/aligned_hashring_test.go +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (c) The Thanos Authors. -// Licensed under the Apache License 2.0. - -package receive - -import ( - "sort" - "strconv" - "strings" - "testing" - - "github.com/efficientgo/core/testutil" - "github.com/stretchr/testify/require" - "github.com/thanos-io/thanos/pkg/store/labelpb" - "github.com/thanos-io/thanos/pkg/store/storepb/prompb" - "github.com/thanos-io/thanos/pkg/strutil" -) - -// podDNS creates a DNS-like string for testing endpoint addresses. -func podDNS(name string, ordinal int) string { - return name + "-" + strconv.Itoa(ordinal) + ".test-svc.test-namespace.svc.cluster.local" -} - -func TestGroupByAZ(t *testing.T) { - // Test setup endpoints. - ep0a := Endpoint{Address: podDNS("pod", 0), AZ: "zone-a"} - ep1a := Endpoint{Address: podDNS("pod", 1), AZ: "zone-a"} - ep2a := Endpoint{Address: podDNS("pod", 2), AZ: "zone-a"} - ep0b := Endpoint{Address: podDNS("pod", 0), AZ: "zone-b"} - ep1b := Endpoint{Address: podDNS("pod", 1), AZ: "zone-b"} - ep0c := Endpoint{Address: podDNS("pod", 0), AZ: "zone-c"} - ep1c := Endpoint{Address: podDNS("pod", 1), AZ: "zone-c"} - invalidEp := Endpoint{Address: "invalid-address-format", AZ: "zone-a"} - duplicateEp0a := Endpoint{Address: podDNS("anotherpod", 0), AZ: "zone-a"} // Same ordinal (0) as ep0a in zone-a. - - testCases := map[string]struct { - inputEndpoints []Endpoint - expectedResult [][]Endpoint - expectError bool - errorContains string - }{ - "error on empty input": { - inputEndpoints: []Endpoint{}, - expectedResult: nil, - expectError: true, - errorContains: "no endpoints provided", - }, - "single AZ, multiple endpoints": { - inputEndpoints: []Endpoint{ep1a, ep0a, ep2a}, - expectedResult: [][]Endpoint{ - {ep0a, ep1a, ep2a}, - }, - expectError: false, - }, - "multiple AZs, balanced and ordered": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b}, - expectedResult: [][]Endpoint{ - {ep0a, ep1a}, - {ep0b, ep1b}, - }, - expectError: false, - }, - "multiple AZs, different counts, stops at first missing ordinal > 0": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep2a, ep0c}, - expectedResult: [][]Endpoint{ - {ep0a}, - {ep0b}, - {ep0c}, - }, - expectError: false, - }, - "error if ordinal 0 missing in any AZ": { - inputEndpoints: []Endpoint{ep1a, ep2a, ep1b}, - expectedResult: nil, - expectError: true, - errorContains: "missing endpoint with ordinal 0", - }, - "error if ordinal 0 missing in only one AZ": { - inputEndpoints: []Endpoint{ep0a, ep1a, ep1b}, - expectedResult: nil, - expectError: true, - errorContains: `AZ "zone-b" is missing endpoint with ordinal 0`, - }, - "error on invalid address format": { - inputEndpoints: []Endpoint{ep0a, invalidEp, ep0b}, - expectedResult: nil, - expectError: true, - errorContains: "failed to extract ordinal from address invalid-address-format", - }, - "error on duplicate ordinal within an AZ": { - inputEndpoints: []Endpoint{ep0a, ep1a, ep0b, duplicateEp0a}, - expectedResult: nil, - expectError: true, - errorContains: "duplicate endpoint ordinal 0 for address " + duplicateEp0a.Address + " in AZ zone-a", - }, - "AZ sorting check": { - inputEndpoints: []Endpoint{ep0b, ep0c, ep0a}, - expectedResult: [][]Endpoint{ - {ep0a}, - {ep0b}, - {ep0c}, - }, - expectError: false, - }, - "multiple AZs, stops correctly when next ordinal missing everywhere": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep0c, ep1c}, - expectedResult: [][]Endpoint{ - {ep0a, ep1a}, - {ep0b, ep1b}, - {ep0c, ep1c}, - }, - expectError: false, - }, - } - - for tcName, tc := range testCases { - t.Run(tcName, func(t *testing.T) { - result, err := groupByAZ(tc.inputEndpoints) - - if tc.expectError { - testutil.NotOk(t, err) - if tc.errorContains != "" { - testutil.Assert(t, strings.Contains(err.Error(), tc.errorContains), "Expected error message to contain '%s', but got: %v", tc.errorContains, err) - } - testutil.Assert(t, result == nil, "Expected nil result on error, got: %v", result) - } else { - testutil.Ok(t, err) - testutil.Equals(t, tc.expectedResult, result) - - // Verify outer slice (AZs) is sorted alphabetically. - if err == nil && len(result) > 1 { - azOrderCorrect := sort.SliceIsSorted(result, func(i, j int) bool { - return result[i][0].AZ < result[j][0].AZ - }) - testutil.Assert(t, azOrderCorrect, "Outer slice is not sorted by AZ") - } - } - }) - } -} - -func TestAlignedKetamaHashringGet(t *testing.T) { - t.Parallel() - - ep0a := Endpoint{Address: podDNS("pod", 0), AZ: "zone-a"} - ep1a := Endpoint{Address: podDNS("pod", 1), AZ: "zone-a"} - ep0b := Endpoint{Address: podDNS("pod", 0), AZ: "zone-b"} - ep1b := Endpoint{Address: podDNS("pod", 1), AZ: "zone-b"} - ep0c := Endpoint{Address: podDNS("pod", 0), AZ: "zone-c"} - ep1c := Endpoint{Address: podDNS("pod", 1), AZ: "zone-c"} - invalidEp := Endpoint{Address: "invalid-address", AZ: "zone-a"} - duplicateEp0a := Endpoint{Address: podDNS("anotherpod", 0), AZ: "zone-a"} - - tsForReplicaTest := &prompb.TimeSeries{ - Labels: []labelpb.ZLabel{{Name: "test", Value: "replica-routing"}}, - } - - testCases := map[string]struct { - inputEndpoints []Endpoint - replicationFactor uint64 - sectionsPerNode int - - tenant string - ts *prompb.TimeSeries - n uint64 - expectedEndpoint Endpoint - - expectConstructorError bool - constructorErrorContains string - expectGetNError bool - getNErrorContains string - }{ - "valid 2 AZs, RF=2, get replica 0": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - tenant: "tenant1", - ts: tsForReplicaTest, - n: 0, - expectedEndpoint: ep0a, - expectConstructorError: false, - expectGetNError: false, - }, - "valid 2 AZs, RF=2, get replica 1": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - tenant: "tenant1", - ts: tsForReplicaTest, - n: 1, - expectedEndpoint: ep0b, - expectConstructorError: false, - expectGetNError: false, - }, - "valid 3 AZs, RF=3, get replica 0": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep0c, ep1c}, - replicationFactor: 3, - sectionsPerNode: SectionsPerNode, - tenant: "tenant1", - ts: tsForReplicaTest, - n: 0, - expectedEndpoint: ep0a, - expectConstructorError: false, - expectGetNError: false, - }, - "valid 3 AZs, RF=3, get replica 1": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep0c, ep1c}, - replicationFactor: 3, - sectionsPerNode: SectionsPerNode, - tenant: "tenant1", - ts: tsForReplicaTest, - n: 1, - expectedEndpoint: ep0b, - expectConstructorError: false, - expectGetNError: false, - }, - "valid 3 AZs, RF=3, get replica 2": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep0c, ep1c}, - replicationFactor: 3, - sectionsPerNode: SectionsPerNode, - tenant: "tenant1", - ts: tsForReplicaTest, - n: 2, - expectedEndpoint: ep0c, - expectConstructorError: false, - expectGetNError: false, - }, - "error: empty input": { - inputEndpoints: []Endpoint{}, - replicationFactor: 1, - sectionsPerNode: SectionsPerNode, - expectConstructorError: true, - constructorErrorContains: "no endpoints provided", - }, - "error: invalid address": { - inputEndpoints: []Endpoint{ep0a, invalidEp, ep0b}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - expectConstructorError: true, - constructorErrorContains: "failed to extract ordinal from address invalid-address", - }, - "error: duplicate ordinal": { - inputEndpoints: []Endpoint{ep0a, ep1a, ep0b, duplicateEp0a}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - expectConstructorError: true, - constructorErrorContains: "duplicate endpoint", - }, - "error: missing ordinal 0": { - inputEndpoints: []Endpoint{ep1a, ep1b}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - expectConstructorError: true, - constructorErrorContains: "failed to group endpoints by AZ: AZ \"zone-a\" is missing endpoint with ordinal 0", - }, - "error: AZ count != RF (too few AZs)": { - inputEndpoints: []Endpoint{ep0a, ep1a}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - expectConstructorError: true, - constructorErrorContains: "number of AZs (1) must equal replication factor (2)", - }, - "error: AZ count != RF (too many AZs)": { - inputEndpoints: []Endpoint{ep0a, ep1a, ep0b, ep1b, ep0c, ep1c}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - expectConstructorError: true, - constructorErrorContains: "number of AZs (3) must equal replication factor (2)", - }, - "constructor success with unbalanced AZs (uses common subset)": { - inputEndpoints: []Endpoint{ep0a, ep1a, ep0b}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - expectConstructorError: false, - }, - "error: GetN index out of bounds (n >= numEndpoints)": { - inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b}, - replicationFactor: 2, - sectionsPerNode: SectionsPerNode, - tenant: "tenant1", - ts: tsForReplicaTest, - n: 4, - expectConstructorError: false, - expectGetNError: true, - getNErrorContains: "insufficient nodes; have 4, want 5", - }, - } - - for tcName, tc := range testCases { - t.Run(tcName, func(t *testing.T) { - hashRing, err := newAlignedKetamaHashring(tc.inputEndpoints, tc.sectionsPerNode, tc.replicationFactor) - - if tc.expectConstructorError { - require.Error(t, err, "Expected constructor error") - require.Nil(t, hashRing, "Hashring should be nil on constructor error") - if tc.constructorErrorContains != "" { - require.Contains(t, err.Error(), tc.constructorErrorContains, "Constructor error message mismatch") - } - return - } - - require.NoError(t, err, "Expected constructor to succeed") - require.NotNil(t, hashRing, "Hashring should not be nil on successful construction") - - if tc.ts == nil && !tc.expectGetNError { - return - } - if tc.ts == nil && tc.expectGetNError { - tc.ts = &prompb.TimeSeries{Labels: []labelpb.ZLabel{{Name: "dummy", Value: "dummy"}}} - } - - result, getNErr := hashRing.GetN(tc.tenant, tc.ts, tc.n) - if tc.expectGetNError { - require.Error(t, getNErr, "Expected GetN error") - if tc.getNErrorContains != "" { - require.Contains(t, getNErr.Error(), tc.getNErrorContains, "GetN error message mismatch") - } - } else { - require.NoError(t, getNErr, "Expected GetN to succeed") - testutil.Equals(t, tc.expectedEndpoint, result, "GetN returned unexpected endpoint") - } - }) - } -} - -func TestAlignedKetamaHashringReplicaOrdinals(t *testing.T) { - t.Parallel() - - var endpoints []Endpoint - for i := 0; i < 20; i++ { - endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-a"}) - } - for i := 0; i < 20; i++ { - endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-b"}) - } - for i := 0; i < 20; i++ { - endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-c"}) - } - replicationFactor := uint64(3) - sectionsPerNode := 10 - - hashRing, err := newAlignedKetamaHashring(endpoints, sectionsPerNode, replicationFactor) - require.NoError(t, err, "Aligned hashring constructor failed") - require.NotNil(t, hashRing, "Hashring should not be nil") - require.NotEmpty(t, hashRing.sections, "Hashring should contain sections") - - // Verify that all replicas within a section have the same ordinal. - for i, s := range hashRing.sections { - if len(s.replicas) == 0 { - continue - } - - expectedOrdinal := -1 - - for replicaNum, replicaIndex := range s.replicas { - require.Less(t, int(replicaIndex), len(hashRing.endpoints), - "Section %d (hash %d), Replica %d: index %d out of bounds for endpoints list (len %d)", - i, s.hash, replicaNum, replicaIndex, len(hashRing.endpoints)) - - endpoint := hashRing.endpoints[replicaIndex] - ordinal, err := strutil.ExtractPodOrdinal(endpoint.Address) - require.NoError(t, err, - "Section %d (hash %d), Replica %d: failed to extract ordinal from address %s", - i, s.hash, replicaNum, endpoint.Address) - - if expectedOrdinal == -1 { - expectedOrdinal = ordinal - } else { - require.Equal(t, expectedOrdinal, ordinal, - "Section %d (hash %d), Replica %d (%s): Mismatched ordinal. Expected %d, got %d. Replicas in section: %v", - i, s.hash, replicaNum, endpoint.Address, expectedOrdinal, ordinal, s.replicas) - } - } - if len(s.replicas) > 0 { - require.NotEqual(t, -1, expectedOrdinal, "Section %d (hash %d): Failed to determine expected ordinal for replicas %v", i, s.hash, s.replicas) - } - } -} diff --git a/pkg/receive/config.go b/pkg/receive/config.go index 75bda21e491..a81508f50f2 100644 --- a/pkg/receive/config.go +++ b/pkg/receive/config.go @@ -67,6 +67,7 @@ type Endpoint struct { Address string `json:"address"` CapNProtoAddress string `json:"capnproto_address"` AZ string `json:"az"` + Shard int `json:"shard"` } func (e *Endpoint) String() string { @@ -113,6 +114,7 @@ func (e *Endpoint) unmarshal(data []byte) error { e.Address = configEndpoint.Address e.AZ = configEndpoint.AZ e.CapNProtoAddress = configEndpoint.CapNProtoAddress + e.Shard = configEndpoint.Shard return nil } @@ -140,13 +142,8 @@ type ShuffleShardingConfig struct { CacheSize int `json:"cache_size"` // ZoneAwarenessDisabled disables zone awareness. We still try to spread the load // across the available zones, but we don't try to balance the shards across zones. - ZoneAwarenessDisabled bool `json:"zone_awareness_disabled"` - // AlignedOrdinalSharding enables aligned ordinal selection for shuffle sharding. - // When true and using aligned_ketama algorithm, the same ordinals are selected - // across all AZs, preserving strict replica alignment. ShardSize represents the - // number of ordinals to select (resulting in ShardSize * numAZs total endpoints). - AlignedOrdinalSharding bool `json:"aligned_ordinal_sharding"` - Overrides []ShuffleShardingOverrideConfig `json:"overrides,omitempty"` + ZoneAwarenessDisabled bool `json:"zone_awareness_disabled"` + Overrides []ShuffleShardingOverrideConfig `json:"overrides,omitempty"` } type tenantMatcher string diff --git a/pkg/receive/handler.go b/pkg/receive/handler.go index 1363177c7fa..7343c926404 100644 --- a/pkg/receive/handler.go +++ b/pkg/receive/handler.go @@ -1740,7 +1740,7 @@ func (p *peerGroup) closeUnlocked(endpoint Endpoint) error { p.connections[endpoint].wp.Close() delete(p.connections, endpoint) if err := c.client.Close(); err != nil { - return fmt.Errorf("closing connection for %s", endpoint) + return fmt.Errorf("closing connection for %v", endpoint) } return nil diff --git a/pkg/receive/handler_test.go b/pkg/receive/handler_test.go index e1f753d38b8..0190d082c52 100644 --- a/pkg/receive/handler_test.go +++ b/pkg/receive/handler_test.go @@ -267,7 +267,7 @@ func (g *fakePeersGroup) close(addr Endpoint) error { func (g *fakePeersGroup) getConnection(_ context.Context, endpoint Endpoint) (WriteableStoreAsyncClient, error) { c, ok := g.clients[endpoint] if !ok { - return nil, fmt.Errorf("client %s not found", endpoint) + return nil, fmt.Errorf("client %v not found", endpoint) } return c, nil } diff --git a/pkg/receive/hashring.go b/pkg/receive/hashring.go index 0b63f9aea8b..dd26e3a50e0 100644 --- a/pkg/receive/hashring.go +++ b/pkg/receive/hashring.go @@ -27,8 +27,6 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/thanos-io/thanos/pkg/store/labelpb" - "github.com/thanos-io/thanos/pkg/strutil" - "github.com/thanos-io/thanos/pkg/store/storepb/prompb" ) @@ -36,9 +34,9 @@ import ( type HashringAlgorithm string const ( - AlgorithmHashmod HashringAlgorithm = "hashmod" - AlgorithmKetama HashringAlgorithm = "ketama" - AlgorithmAlignedKetama HashringAlgorithm = "aligned_ketama" + AlgorithmHashmod HashringAlgorithm = "hashmod" + AlgorithmKetama HashringAlgorithm = "ketama" + AlgorithmRendezvous HashringAlgorithm = "rendezvous" // SectionsPerNode is the number of sections in the ring assigned to each node // in the ketama hashring. A higher number yields a better series distribution, @@ -366,7 +364,7 @@ type shuffleShardHashring struct { nodes []Endpoint // cache stores tenant-specific subrings. The value is Hashring to support both - // *ketamaHashring (regular shuffle sharding) and aligned ketama subrings. + // *ketamaHashring (regular shuffle sharding) and *rendezvousHashring subrings. cache *lru.Cache[string, Hashring] metrics *shuffleShardCacheMetrics @@ -594,9 +592,10 @@ func (s *shuffleShardHashring) getTenantShardCached(tenant string) (Hashring, er var h Hashring var err error - if s.shuffleShardingConfig.AlignedOrdinalSharding { - h, err = s.getTenantShardAligned(tenant) - } else { + switch s.baseRing.(type) { + case *rendezvousHashring: + h, err = s.getTenantShardRendezvous(tenant) + default: h, err = s.getTenantShard(tenant) } if err != nil { @@ -710,248 +709,207 @@ func (s *shuffleShardHashring) getTenantShard(tenant string) (*ketamaHashring, e return newKetamaHashring(finalNodes, SectionsPerNode, s.replicationFactor) } -// ordinalSection represents a section in the ordinal ring for consistent hashing. -type ordinalSection struct { - ordinal int - hash uint64 -} +// groupByAZ groups endpoints by Availability Zone and sorts them by shard. +// It returns a 2D slice where each inner slice represents an AZ (sorted alphabetically) +// and contains endpoints sorted by shard. All inner slices are truncated to the +// length of the largest common sequence of shards starting from 0 across all AZs. +func groupByAZ(endpoints []Endpoint) ([][]Endpoint, error) { + if len(endpoints) == 0 { + return nil, errors.New("no endpoints provided") + } + + // Group endpoints by AZ and then by shard. + azEndpoints := make(map[string]map[int]Endpoint) + for _, ep := range endpoints { + if _, ok := azEndpoints[ep.AZ]; !ok { + azEndpoints[ep.AZ] = make(map[int]Endpoint) + } + if _, exists := azEndpoints[ep.AZ][ep.Shard]; exists { + return nil, fmt.Errorf("duplicate endpoint shard %d for address %s in AZ %s", ep.Shard, ep.Address, ep.AZ) + } + azEndpoints[ep.AZ][ep.Shard] = ep + } -type ordinalSections []ordinalSection + // Get sorted list of AZ names. + sortedAZs := make([]string, 0, len(azEndpoints)) + for az := range azEndpoints { + sortedAZs = append(sortedAZs, az) + } + sort.Strings(sortedAZs) -func (o ordinalSections) Len() int { return len(o) } -func (o ordinalSections) Less(i, j int) bool { return o[i].hash < o[j].hash } -func (o ordinalSections) Swap(i, j int) { o[i], o[j] = o[j], o[i] } + // Determine the maximum common shard across all AZs. + maxCommonShard := -1 + for i := 0; ; i++ { + presentInAllAZs := true + for _, az := range sortedAZs { + if _, ok := azEndpoints[az][i]; !ok { + presentInAllAZs = false + if i == 0 { + return nil, fmt.Errorf("AZ %q is missing endpoint with shard 0", az) + } + break + } + } + if !presentInAllAZs { + maxCommonShard = i - 1 + break + } + } + if maxCommonShard < 0 { + return nil, errors.New("no common endpoints with shard 0 found across all AZs") + } + numAZs := len(sortedAZs) + result := make([][]Endpoint, numAZs) + for i, az := range sortedAZs { + result[i] = make([]Endpoint, 0, maxCommonShard+1) + for j := 0; j <= maxCommonShard; j++ { + result[i] = append(result[i], azEndpoints[az][j]) + } + } + return result, nil +} -// extractOrdinalStructure extracts the ordinal-to-endpoint mapping per AZ -// and returns the set of ordinals common to all AZs. -func extractOrdinalStructure(endpoints []Endpoint) (map[string]map[int]Endpoint, []int, error) { +// extractShardStructure extracts the shard-to-endpoint mapping per AZ +// and returns the set of shards common to all AZs. +func extractShardStructure(endpoints []Endpoint) (map[string]map[int]Endpoint, []int, error) { if len(endpoints) == 0 { return nil, nil, errors.New("no endpoints provided") } - // Group endpoints by AZ and ordinal - azOrdinalMap := make(map[string]map[int]Endpoint) + // Group endpoints by AZ and shard + azShardMap := make(map[string]map[int]Endpoint) for _, ep := range endpoints { - ordinal, err := strutil.ExtractPodOrdinal(ep.Address) - if err != nil { - return nil, nil, errors.Wrapf(err, "failed to extract ordinal from address %s", ep.Address) - } - if _, ok := azOrdinalMap[ep.AZ]; !ok { - azOrdinalMap[ep.AZ] = make(map[int]Endpoint) + if _, ok := azShardMap[ep.AZ]; !ok { + azShardMap[ep.AZ] = make(map[int]Endpoint) } - azOrdinalMap[ep.AZ][ordinal] = ep + azShardMap[ep.AZ][ep.Shard] = ep } - if len(azOrdinalMap) == 0 { + if len(azShardMap) == 0 { return nil, nil, errors.New("no AZs found") } - // Find common ordinals across all AZs - var commonOrdinals []int + // Find common shards across all AZs + var commonShards []int var firstAZ string - for az := range azOrdinalMap { + for az := range azShardMap { firstAZ = az break } - for ordinal := range azOrdinalMap[firstAZ] { + for shard := range azShardMap[firstAZ] { presentInAll := true - for az, ordMap := range azOrdinalMap { + for az, shardMap := range azShardMap { if az == firstAZ { continue } - if _, ok := ordMap[ordinal]; !ok { + if _, ok := shardMap[shard]; !ok { presentInAll = false break } } if presentInAll { - commonOrdinals = append(commonOrdinals, ordinal) + commonShards = append(commonShards, shard) } } - if len(commonOrdinals) == 0 { - return nil, nil, errors.New("no common ordinals found across all AZs") + if len(commonShards) == 0 { + return nil, nil, errors.New("no common shards found across all AZs") } - sort.Ints(commonOrdinals) - return azOrdinalMap, commonOrdinals, nil + sort.Ints(commonShards) + return azShardMap, commonShards, nil } -// buildOrdinalRing creates a consistent hash ring of ordinals. -// Each ordinal gets multiple sections for better distribution. -func buildOrdinalRing(ordinals []int, sectionsPerOrdinal int) ordinalSections { - ring := make(ordinalSections, 0, len(ordinals)*sectionsPerOrdinal) - hasher := xxhash.New() - - for _, ordinal := range ordinals { - for i := 1; i <= sectionsPerOrdinal; i++ { - hasher.Reset() - _, _ = hasher.Write([]byte(fmt.Sprintf("ordinal-%d:%d", ordinal, i))) - ring = append(ring, ordinalSection{ - ordinal: ordinal, - hash: hasher.Sum64(), - }) - } - } - - sort.Sort(ring) - return ring -} - -// selectOrdinalsConsistent selects ordinals using consistent hashing. -// This provides stability: adding ordinal N only affects tenants that would hash near N. -func selectOrdinalsConsistent(ring ordinalSections, tenant string, count int) []int { - if count >= len(ring) { - // Return all unique ordinals if count exceeds ring size - seen := make(map[int]struct{}) - for _, sec := range ring { - seen[sec.ordinal] = struct{}{} - } - result := make([]int, 0, len(seen)) - for ord := range seen { - result = append(result, ord) - } - sort.Ints(result) +// selectShardsRendezvous selects shards using rendezvous (highest random weight) hashing. +// For each shard, it computes hash(tenant, shard) and picks the top-K shards with the highest values. +func selectShardsRendezvous(commonShards []int, tenant string, count int) []int { + if count >= len(commonShards) { + result := make([]int, len(commonShards)) + copy(result, commonShards) return result } - seed := ShuffleShardSeed(tenant, "") // No AZ suffix for alignment - r := rand.New(rand.NewSource(seed)) + hasher := xxhash.New() - selected := make(map[int]struct{}) - result := make([]int, 0, count) + type shardScore struct { + shard int + score uint64 + } + scores := make([]shardScore, len(commonShards)) + for i, shard := range commonShards { + hasher.Reset() + _, _ = hasher.Write([]byte(tenant)) + _, _ = hasher.Write([]byte(strconv.Itoa(shard))) + scores[i] = shardScore{shard: shard, score: hasher.Sum64()} + } - for len(result) < count { - pos := r.Uint64() - idx := sort.Search(len(ring), func(i int) bool { - return ring[i].hash >= pos - }) - if idx == len(ring) { - idx = 0 - } + // Partial sort: find top-K by score (descending). + sort.Slice(scores, func(i, j int) bool { + return scores[i].score > scores[j].score + }) - // Walk ring to find unselected ordinal - for j := 0; j < len(ring); j++ { - checkIdx := (idx + j) % len(ring) - ord := ring[checkIdx].ordinal - if _, ok := selected[ord]; !ok { - selected[ord] = struct{}{} - result = append(result, ord) - break - } - } + result := make([]int, count) + for i := 0; i < count; i++ { + result[i] = scores[i].shard } - sort.Ints(result) return result } -// getTenantShardAligned returns a tenant shard with aligned ordinals across all AZs. -// Unlike getTenantShard which selects nodes independently per AZ, this selects -// ordinals first, then takes the same ordinal from each AZ. -func (s *shuffleShardHashring) getTenantShardAligned(tenant string) (Hashring, error) { - // Extract ordinal structure from all nodes - azOrdinalMap, commonOrdinals, err := extractOrdinalStructure(s.nodes) +// getTenantShardRendezvous returns a tenant shard with aligned shards across all AZs +// using rendezvous hashing for shard selection. Unlike getTenantShard which selects nodes +// independently per AZ, this selects shards first, then takes the same shard from each AZ. +func (s *shuffleShardHashring) getTenantShardRendezvous(tenant string) (Hashring, error) { + // Extract shard structure from all nodes + azShardMap, commonShards, err := extractShardStructure(s.nodes) if err != nil { - return nil, errors.Wrap(err, "failed to extract ordinal structure") + return nil, errors.Wrap(err, "failed to extract shard structure") } - // Determine shard size (number of ordinals to select) - shardSize := s.getShardSize(tenant) - if shardSize > len(commonOrdinals) { - return nil, fmt.Errorf("shard size %d exceeds available common ordinals (%d)", shardSize, len(commonOrdinals)) + // shard_size is the total shard size; divide by numAZs to get per-AZ count. + totalShardSize := s.getShardSize(tenant) + numAZs := len(azShardMap) + perAZShards := totalShardSize / numAZs // floor + if perAZShards == 0 { + return nil, fmt.Errorf("shard size %d too small for %d AZs", totalShardSize, numAZs) + } + if perAZShards > len(commonShards) { + return nil, fmt.Errorf("per-AZ shard count %d (from total %d / %d AZs) exceeds available common shards (%d)", perAZShards, totalShardSize, numAZs, len(commonShards)) } - // Build ordinal ring for consistent hashing - // Use fewer sections per ordinal since we have fewer ordinals than nodes - sectionsPerOrdinal := SectionsPerNode - ordinalRing := buildOrdinalRing(commonOrdinals, sectionsPerOrdinal) - - // Select ordinals using consistent hashing - selectedOrdinals := selectOrdinalsConsistent(ordinalRing, tenant, shardSize) + // Select shards using rendezvous hashing + selectedShards := selectShardsRendezvous(commonShards, tenant, perAZShards) - // Build endpoint list with same ordinals from each AZ - // Sorted AZ order for deterministic endpoint ordering - sortedAZs := make([]string, 0, len(azOrdinalMap)) - for az := range azOrdinalMap { + // Build endpoint list with same shards from each AZ + sortedAZs := make([]string, 0, len(azShardMap)) + for az := range azShardMap { sortedAZs = append(sortedAZs, az) } sort.Strings(sortedAZs) - // Create aligned ketama subring manually to preserve ordinal alignment - // without requiring sequential ordinals starting from 0 - return newAlignedSubring(azOrdinalMap, sortedAZs, selectedOrdinals, SectionsPerNode, s.replicationFactor) -} - -// newAlignedSubring creates a ketama hashring with aligned replicas from a subset of ordinals. -// Unlike newAlignedKetamaHashring, this doesn't require ordinals to be sequential from 0. -// The alignment property: for any section, all replicas have the same ordinal across different AZs. -func newAlignedSubring( - azOrdinalMap map[string]map[int]Endpoint, - sortedAZs []string, - selectedOrdinals []int, - sectionsPerNode int, - replicationFactor uint64, -) (*ketamaHashring, error) { - numAZs := len(sortedAZs) - numOrdinals := len(selectedOrdinals) - - if uint64(numAZs) != replicationFactor { - return nil, fmt.Errorf("number of AZs (%d) must equal replication factor (%d)", numAZs, replicationFactor) - } - - // Build flat endpoint list: [AZ0-ord0, AZ0-ord1, ..., AZ1-ord0, AZ1-ord1, ...] - // where ordN refers to selectedOrdinals[N], not the actual ordinal value - totalEndpoints := numAZs * numOrdinals - flatEndpoints := make([]Endpoint, 0, totalEndpoints) - for _, az := range sortedAZs { - for _, ordinal := range selectedOrdinals { - ep, ok := azOrdinalMap[az][ordinal] + // Build azEndpoints directly (non-contiguous shards are OK for sub-rings). + numShards := len(selectedShards) + azEndpoints := make([][]Endpoint, len(sortedAZs)) + var flatEndpoints []Endpoint + for i, az := range sortedAZs { + azEndpoints[i] = make([]Endpoint, 0, numShards) + for _, shard := range selectedShards { + ep, ok := azShardMap[az][shard] if !ok { - return nil, fmt.Errorf("ordinal %d not found in AZ %s", ordinal, az) + return nil, fmt.Errorf("shard %d not found in AZ %s", shard, az) } - flatEndpoints = append(flatEndpoints, ep) + azEndpoints[i] = append(azEndpoints[i], ep) } + flatEndpoints = append(flatEndpoints, azEndpoints[i]...) } - // Create sections with aligned replicas - // For aligned subring, we create sections based on the first AZ's endpoints (primary) - // Each section's replicas point to the same "position" (ordinal index) in each AZ - hasher := xxhash.New() - ringSections := make(sections, 0, numOrdinals*sectionsPerNode) - - for ordinalIdx := 0; ordinalIdx < numOrdinals; ordinalIdx++ { - // Primary endpoint is from the first AZ - primaryEndpoint := flatEndpoints[ordinalIdx] // AZ0 endpoints are at indices 0..numOrdinals-1 - - for sectionIdx := 1; sectionIdx <= sectionsPerNode; sectionIdx++ { - hasher.Reset() - _, _ = hasher.Write([]byte(primaryEndpoint.Address + ":" + strconv.Itoa(sectionIdx))) - - sec := §ion{ - hash: hasher.Sum64(), - az: primaryEndpoint.AZ, - endpointIndex: uint64(ordinalIdx), // Index within first AZ - replicas: make([]uint64, 0, replicationFactor), - } - - // Add replicas: same ordinal index from each AZ - for azIdx := 0; azIdx < numAZs; azIdx++ { - replicaFlatIndex := azIdx*numOrdinals + ordinalIdx - sec.replicas = append(sec.replicas, uint64(replicaFlatIndex)) - } - - ringSections = append(ringSections, sec) - } - } - - sort.Sort(ringSections) - - return &ketamaHashring{ - endpoints: flatEndpoints, - sections: ringSections, - numEndpoints: uint64(totalEndpoints), + return &rendezvousHashring{ + azEndpoints: azEndpoints, + sortedAZs: sortedAZs, + numShards: numShards, + replicationFactor: s.replicationFactor, + flatEndpoints: flatEndpoints, }, nil } @@ -979,6 +937,11 @@ func NewMultiHashring(algorithm HashringAlgorithm, replicationFactor uint64, cfg cache: make(map[string]Hashring), } + numShardsGauge := promauto.With(reg).NewGaugeVec(prometheus.GaugeOpts{ + Name: "thanos_receive_hashring_shards", + Help: "Number of shards per hashring after groupByAZ alignment.", + }, []string{"hashring"}) + for _, h := range cfg { var hashring Hashring var err error @@ -986,7 +949,7 @@ func NewMultiHashring(algorithm HashringAlgorithm, replicationFactor uint64, cfg if h.Algorithm != "" { activeAlgorithm = h.Algorithm } - hashring, err = newHashring(activeAlgorithm, h.Endpoints, replicationFactor, h.Hashring, h.Tenants, h.ShuffleShardingConfig, reg) + hashring, err = newHashring(activeAlgorithm, h.Endpoints, replicationFactor, h.Hashring, h.Tenants, h.ShuffleShardingConfig, reg, numShardsGauge) if err != nil { return nil, err } @@ -1007,7 +970,7 @@ func NewMultiHashring(algorithm HashringAlgorithm, replicationFactor uint64, cfg return m, nil } -func newHashring(algorithm HashringAlgorithm, endpoints []Endpoint, replicationFactor uint64, hashring string, tenants []string, shuffleShardingConfig ShuffleShardingConfig, reg prometheus.Registerer) (Hashring, error) { +func newHashring(algorithm HashringAlgorithm, endpoints []Endpoint, replicationFactor uint64, hashring string, tenants []string, shuffleShardingConfig ShuffleShardingConfig, reg prometheus.Registerer, numShardsGauge *prometheus.GaugeVec) (Hashring, error) { switch algorithm { case AlgorithmHashmod: @@ -1031,11 +994,12 @@ func newHashring(algorithm HashringAlgorithm, endpoints []Endpoint, replicationF return newShuffleShardHashring(ringImpl, shuffleShardingConfig, replicationFactor, reg, hashring) } return ringImpl, nil - case AlgorithmAlignedKetama: - ringImpl, err := newAlignedKetamaHashring(endpoints, SectionsPerNode, replicationFactor) + case AlgorithmRendezvous: + ringImpl, err := newRendezvousHashring(endpoints, replicationFactor) if err != nil { return nil, err } + numShardsGauge.WithLabelValues(hashring).Set(float64(ringImpl.numShards)) if shuffleShardingConfig.ShardSize > 0 { if shuffleShardingConfig.ShardSize > len(endpoints) { return nil, fmt.Errorf("shard size %d is larger than number of nodes in hashring %s (%d)", shuffleShardingConfig.ShardSize, hashring, len(endpoints)) diff --git a/pkg/receive/hashring_test.go b/pkg/receive/hashring_test.go index 379861420e8..145c3fc7a2e 100644 --- a/pkg/receive/hashring_test.go +++ b/pkg/receive/hashring_test.go @@ -6,6 +6,8 @@ package receive import ( "fmt" "math" + "sort" + "strconv" "strings" "testing" @@ -1047,409 +1049,118 @@ func compareNodeSets(before, after map[string]struct{}) (added, removed []string return } -// makeK8sEndpoint creates an endpoint with K8s-style DNS name that has an extractable ordinal. -func makeK8sEndpoint(podName string, ordinal int, az string) Endpoint { - return Endpoint{ - Address: fmt.Sprintf("%s-%d.svc.test.svc.cluster.local:10901", podName, ordinal), - AZ: az, - } -} - -func TestAlignedOrdinalShardingBasic(t *testing.T) { - t.Parallel() - - // Create 3 AZs with 5 ordinals each (15 total endpoints) - endpoints := make([]Endpoint, 0, 15) - azs := []string{"az-a", "az-b", "az-c"} - for _, az := range azs { - for ord := 0; ord < 5; ord++ { - endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) - } - } - - // Create aligned ketama base ring with RF=3 (one per AZ) - baseRing, err := newAlignedKetamaHashring(endpoints, SectionsPerNode, 3) - require.NoError(t, err) - - // Create shuffle shard hashring with aligned ordinal sharding enabled - cfg := ShuffleShardingConfig{ - ShardSize: 2, // Select 2 ordinals -> 6 endpoints (2 * 3 AZs) - AlignedOrdinalSharding: true, - } - shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-aligned") - require.NoError(t, err) - - // Get the tenant shard - tenant := "test-tenant" - shard, err := shardRing.getTenantShardAligned(tenant) - require.NoError(t, err) - - // Verify we got the right number of nodes (2 ordinals * 3 AZs = 6) - nodes := shard.Nodes() - require.Len(t, nodes, 6, "expected 6 endpoints (2 ordinals * 3 AZs)") - - // Extract ordinals from each AZ and verify they're the same - ordinalsByAZ := make(map[string][]int) - for _, node := range nodes { - ordinalsByAZ[node.AZ] = append(ordinalsByAZ[node.AZ], extractOrdinalFromAddress(t, node.Address)) - } - - // Verify each AZ has exactly 2 ordinals - require.Len(t, ordinalsByAZ, 3, "expected 3 AZs") - for az, ordinals := range ordinalsByAZ { - require.Len(t, ordinals, 2, "AZ %s should have 2 ordinals", az) - } - - // Verify all AZs have the SAME ordinals (the key invariant) - var referenceOrdinals []int - for _, ordinals := range ordinalsByAZ { - if referenceOrdinals == nil { - referenceOrdinals = ordinals - } else { - require.ElementsMatch(t, referenceOrdinals, ordinals, - "all AZs should have the same ordinals for aligned ordinal sharding") - } - } - - t.Logf("Selected ordinals: %v", referenceOrdinals) -} - -func TestAlignedOrdinalShardingConsistency(t *testing.T) { - t.Parallel() - - // Create 3 AZs with 5 ordinals each - endpoints := make([]Endpoint, 0, 15) - azs := []string{"az-a", "az-b", "az-c"} - for _, az := range azs { - for ord := 0; ord < 5; ord++ { - endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) - } - } - - baseRing, err := newAlignedKetamaHashring(endpoints, SectionsPerNode, 3) - require.NoError(t, err) - - cfg := ShuffleShardingConfig{ - ShardSize: 2, - AlignedOrdinalSharding: true, - } - shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-consistency") - require.NoError(t, err) - - // Verify same tenant always gets same ordinals across multiple calls - tenant := "consistent-tenant" - var firstOrdinals []int - - for trial := 0; trial < 10; trial++ { - shard, err := shardRing.getTenantShardAligned(tenant) - require.NoError(t, err) - - currentOrdinals := extractOrdinalsFromShard(t, shard) - if firstOrdinals == nil { - firstOrdinals = currentOrdinals - } else { - require.Equal(t, firstOrdinals, currentOrdinals, - "same tenant should always get same ordinals") - } - } +// podDNS creates a DNS-like string for testing endpoint addresses. +func podDNS(name string, shard int) string { + return name + "-" + strconv.Itoa(shard) + ".test-svc.test-namespace.svc.cluster.local" } -func TestAlignedOrdinalShardingDifferentTenants(t *testing.T) { - t.Parallel() - - // Create 3 AZs with 10 ordinals each to have enough spread - endpoints := make([]Endpoint, 0, 30) - azs := []string{"az-a", "az-b", "az-c"} - for _, az := range azs { - for ord := 0; ord < 10; ord++ { - endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) - } - } - - baseRing, err := newAlignedKetamaHashring(endpoints, SectionsPerNode, 3) - require.NoError(t, err) - - cfg := ShuffleShardingConfig{ - ShardSize: 3, // Select 3 ordinals - AlignedOrdinalSharding: true, - } - shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-diff-tenants") - require.NoError(t, err) - - // Different tenants should (likely) get different ordinals - tenantOrdinals := make(map[string][]int) - numTenants := 20 - - for i := 0; i < numTenants; i++ { - tenant := fmt.Sprintf("tenant-%d", i) - shard, err := shardRing.getTenantShardAligned(tenant) - require.NoError(t, err) - tenantOrdinals[tenant] = extractOrdinalsFromShard(t, shard) - } - - // Count unique ordinal sets - uniqueSets := make(map[string]int) - for _, ordinals := range tenantOrdinals { - key := fmt.Sprintf("%v", ordinals) - uniqueSets[key]++ - } - - // With 10 ordinals choosing 3, there are C(10,3)=120 possible combinations - // We expect multiple unique sets across 20 tenants - t.Logf("Unique ordinal sets: %d out of %d tenants", len(uniqueSets), numTenants) - require.Greater(t, len(uniqueSets), 1, "different tenants should get different ordinal sets") -} - -func TestAlignedOrdinalShardingPreservesAlignment(t *testing.T) { - t.Parallel() - - // Create 3 AZs with 5 ordinals each - endpoints := make([]Endpoint, 0, 15) - azs := []string{"az-a", "az-b", "az-c"} - for _, az := range azs { - for ord := 0; ord < 5; ord++ { - endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) - } - } - - baseRing, err := newAlignedKetamaHashring(endpoints, SectionsPerNode, 3) - require.NoError(t, err) - - cfg := ShuffleShardingConfig{ - ShardSize: 2, - AlignedOrdinalSharding: true, - } - shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-preserves") - require.NoError(t, err) - - tenant := "alignment-test-tenant" - - // Use GetN to get replicas and verify they're aligned (same ordinal across AZs) - for i := 0; i < 100; i++ { - ts := &prompb.TimeSeries{ - Labels: []labelpb.ZLabel{ - {Name: "series", Value: fmt.Sprintf("series-%d", i)}, +func TestGroupByAZ(t *testing.T) { + // Test setup endpoints. + ep0a := Endpoint{Address: podDNS("pod", 0), AZ: "zone-a", Shard: 0} + ep1a := Endpoint{Address: podDNS("pod", 1), AZ: "zone-a", Shard: 1} + ep2a := Endpoint{Address: podDNS("pod", 2), AZ: "zone-a", Shard: 2} + ep0b := Endpoint{Address: podDNS("pod", 0), AZ: "zone-b", Shard: 0} + ep1b := Endpoint{Address: podDNS("pod", 1), AZ: "zone-b", Shard: 1} + ep0c := Endpoint{Address: podDNS("pod", 0), AZ: "zone-c", Shard: 0} + ep1c := Endpoint{Address: podDNS("pod", 1), AZ: "zone-c", Shard: 1} + duplicateEp0a := Endpoint{Address: podDNS("anotherpod", 0), AZ: "zone-a", Shard: 0} // Same shard (0) as ep0a in zone-a. + + testCases := map[string]struct { + inputEndpoints []Endpoint + expectedResult [][]Endpoint + expectError bool + errorContains string + }{ + "error on empty input": { + inputEndpoints: []Endpoint{}, + expectedResult: nil, + expectError: true, + errorContains: "no endpoints provided", + }, + "single AZ, multiple endpoints": { + inputEndpoints: []Endpoint{ep1a, ep0a, ep2a}, + expectedResult: [][]Endpoint{ + {ep0a, ep1a, ep2a}, }, - } - - // Get all 3 replicas (RF=3) - var replicas []Endpoint - for n := uint64(0); n < 3; n++ { - ep, err := shardRing.GetN(tenant, ts, n) - require.NoError(t, err) - replicas = append(replicas, ep) - } - - // Verify all 3 replicas have the same ordinal but different AZs - ordinals := make(map[int]struct{}) - azsSeen := make(map[string]struct{}) - for _, ep := range replicas { - ordinals[extractOrdinalFromAddress(t, ep.Address)] = struct{}{} - azsSeen[ep.AZ] = struct{}{} - } - - require.Len(t, ordinals, 1, "all replicas should have the same ordinal for series %d", i) - require.Len(t, azsSeen, 3, "replicas should span all 3 AZs for series %d", i) - } -} - -// TestAlignedOrdinalShardingDataDistribution verifies the key behavior: -// - With shard_size=2, tenant gets 2 ordinals (e.g., ordinals 1 and 4) -// - Series are distributed across both ordinals -// - a-1, b-1, c-1 always receive the same series (aligned replicas for ordinal 1) -// - a-4, b-4, c-4 always receive the same series (aligned replicas for ordinal 4) -// - Series assigned to ordinal 1 are different from series assigned to ordinal 4. -func TestAlignedOrdinalShardingDataDistribution(t *testing.T) { - t.Parallel() - - // Create 3 AZs with 5 ordinals each - endpoints := make([]Endpoint, 0, 15) - azs := []string{"az-a", "az-b", "az-c"} - for _, az := range azs { - for ord := 0; ord < 5; ord++ { - endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) - } - } - - baseRing, err := newAlignedKetamaHashring(endpoints, SectionsPerNode, 3) - require.NoError(t, err) - - cfg := ShuffleShardingConfig{ - ShardSize: 2, // Select 2 ordinals - AlignedOrdinalSharding: true, - } - shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-distribution") - require.NoError(t, err) - - tenant := "distribution-test-tenant" - - // First, get the tenant's selected ordinals - shard, err := shardRing.getTenantShardAligned(tenant) - require.NoError(t, err) - selectedOrdinals := extractOrdinalsFromShard(t, shard) - require.Len(t, selectedOrdinals, 2, "tenant should have exactly 2 ordinals") - t.Logf("Tenant's selected ordinals: %v", selectedOrdinals) - - // Track which series go to which ordinal - // Key: ordinal, Value: set of series indices - seriesByOrdinal := make(map[int]map[int]struct{}) - for _, ord := range selectedOrdinals { - seriesByOrdinal[ord] = make(map[int]struct{}) - } - - // Track which endpoints receive which series - // Key: endpoint address, Value: set of series indices - seriesByEndpoint := make(map[string]map[int]struct{}) - - // Generate many series and track their distribution - numSeries := 1000 - for i := 0; i < numSeries; i++ { - ts := &prompb.TimeSeries{ - Labels: []labelpb.ZLabel{ - {Name: "series", Value: fmt.Sprintf("series-%d", i)}, - {Name: "__name__", Value: "test_metric"}, + expectError: false, + }, + "multiple AZs, balanced and ordered": { + inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b}, + expectedResult: [][]Endpoint{ + {ep0a, ep1a}, + {ep0b, ep1b}, }, - } - - // Get all 3 replicas - var replicas []Endpoint - for n := uint64(0); n < 3; n++ { - ep, err := shardRing.GetN(tenant, ts, n) - require.NoError(t, err) - replicas = append(replicas, ep) - - // Track series per endpoint - if seriesByEndpoint[ep.Address] == nil { - seriesByEndpoint[ep.Address] = make(map[int]struct{}) - } - seriesByEndpoint[ep.Address][i] = struct{}{} - } - - // All replicas should have the same ordinal - ordinal := extractOrdinalFromAddress(t, replicas[0].Address) - for _, ep := range replicas[1:] { - epOrdinal := extractOrdinalFromAddress(t, ep.Address) - require.Equal(t, ordinal, epOrdinal, "all replicas for series %d should have same ordinal", i) - } - - // Track which ordinal this series went to - seriesByOrdinal[ordinal][i] = struct{}{} - } - - // Verify 1: Series are distributed across BOTH ordinals (not just one) - for ord, series := range seriesByOrdinal { - t.Logf("Ordinal %d received %d series", ord, len(series)) - require.Greater(t, len(series), 0, "ordinal %d should receive some series", ord) - } - - // Verify 2: Same ordinal across different AZs receives the SAME series - // Group endpoints by ordinal - endpointsByOrdinal := make(map[int][]string) - for addr := range seriesByEndpoint { - ord := extractOrdinalFromAddress(t, addr) - endpointsByOrdinal[ord] = append(endpointsByOrdinal[ord], addr) - } - - for ord, addrs := range endpointsByOrdinal { - if len(addrs) < 2 { - continue - } - // All endpoints with the same ordinal should have received the exact same series - referenceSeries := seriesByEndpoint[addrs[0]] - for _, addr := range addrs[1:] { - otherSeries := seriesByEndpoint[addr] - require.Equal(t, len(referenceSeries), len(otherSeries), - "endpoints with ordinal %d should have same number of series", ord) - for seriesIdx := range referenceSeries { - _, ok := otherSeries[seriesIdx] - require.True(t, ok, - "series %d should be on all endpoints with ordinal %d", seriesIdx, ord) - } - } - t.Logf("Verified: All %d endpoints with ordinal %d have identical %d series", - len(addrs), ord, len(referenceSeries)) - } - - // Verify 3: Different ordinals receive DIFFERENT series (no overlap) - ordinalList := make([]int, 0, len(seriesByOrdinal)) - for ord := range seriesByOrdinal { - ordinalList = append(ordinalList, ord) - } - if len(ordinalList) >= 2 { - series1 := seriesByOrdinal[ordinalList[0]] - series2 := seriesByOrdinal[ordinalList[1]] - for seriesIdx := range series1 { - _, overlap := series2[seriesIdx] - require.False(t, overlap, - "series %d should not be on both ordinal %d and ordinal %d", - seriesIdx, ordinalList[0], ordinalList[1]) - } - t.Logf("Verified: Ordinals %d and %d have no overlapping series", ordinalList[0], ordinalList[1]) - } -} - -func TestAlignedOrdinalShardingValidation(t *testing.T) { - t.Parallel() - - endpoints := make([]Endpoint, 0, 15) - azs := []string{"az-a", "az-b", "az-c"} - for _, az := range azs { - for ord := 0; ord < 5; ord++ { - endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) - } - } - - baseRing, err := newAlignedKetamaHashring(endpoints, SectionsPerNode, 3) - require.NoError(t, err) - - // Test shard size exceeding available ordinals - cfg := ShuffleShardingConfig{ - ShardSize: 10, // Only 5 ordinals available - AlignedOrdinalSharding: true, + expectError: false, + }, + "multiple AZs, different counts, stops at first missing shard > 0": { + inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep2a, ep0c}, + expectedResult: [][]Endpoint{ + {ep0a}, + {ep0b}, + {ep0c}, + }, + expectError: false, + }, + "error if shard 0 missing in any AZ": { + inputEndpoints: []Endpoint{ep1a, ep2a, ep1b}, + expectedResult: nil, + expectError: true, + errorContains: "missing endpoint with shard 0", + }, + "error if shard 0 missing in only one AZ": { + inputEndpoints: []Endpoint{ep0a, ep1a, ep1b}, + expectedResult: nil, + expectError: true, + errorContains: `AZ "zone-b" is missing endpoint with shard 0`, + }, + "error on duplicate shard within an AZ": { + inputEndpoints: []Endpoint{ep0a, ep1a, ep0b, duplicateEp0a}, + expectedResult: nil, + expectError: true, + errorContains: "duplicate endpoint shard 0 for address " + duplicateEp0a.Address + " in AZ zone-a", + }, + "AZ sorting check": { + inputEndpoints: []Endpoint{ep0b, ep0c, ep0a}, + expectedResult: [][]Endpoint{ + {ep0a}, + {ep0b}, + {ep0c}, + }, + expectError: false, + }, + "multiple AZs, stops correctly when next shard missing everywhere": { + inputEndpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep0c, ep1c}, + expectedResult: [][]Endpoint{ + {ep0a, ep1a}, + {ep0b, ep1b}, + {ep0c, ep1c}, + }, + expectError: false, + }, } - shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-validation") - require.NoError(t, err) - _, err = shardRing.getTenantShardAligned("test-tenant") - require.Error(t, err) - require.Contains(t, err.Error(), "exceeds available common ordinals") -} + for tcName, tc := range testCases { + t.Run(tcName, func(t *testing.T) { + result, err := groupByAZ(tc.inputEndpoints) -// Helper function to extract ordinal from K8s-style address. -func extractOrdinalFromAddress(t *testing.T, address string) int { - t.Helper() - // Address format: pod-az-N.svc.test.svc.cluster.local:10901 - parts := strings.Split(address, ".") - require.Greater(t, len(parts), 0) - podPart := parts[0] // pod-az-N - lastDash := strings.LastIndex(podPart, "-") - require.Greater(t, lastDash, 0) - ordinalStr := podPart[lastDash+1:] - var ordinal int - _, err := fmt.Sscanf(ordinalStr, "%d", &ordinal) - require.NoError(t, err) - return ordinal -} - -// Helper function to extract unique ordinals from a shard. -func extractOrdinalsFromShard(t *testing.T, shard Hashring) []int { - t.Helper() - nodes := shard.Nodes() - ordinalSet := make(map[int]struct{}) - for _, node := range nodes { - ordinalSet[extractOrdinalFromAddress(t, node.Address)] = struct{}{} - } - ordinals := make([]int, 0, len(ordinalSet)) - for ord := range ordinalSet { - ordinals = append(ordinals, ord) - } - // Sort for consistent comparison - for i := 0; i < len(ordinals); i++ { - for j := i + 1; j < len(ordinals); j++ { - if ordinals[i] > ordinals[j] { - ordinals[i], ordinals[j] = ordinals[j], ordinals[i] + if tc.expectError { + testutil.NotOk(t, err) + if tc.errorContains != "" { + testutil.Assert(t, strings.Contains(err.Error(), tc.errorContains), "Expected error message to contain '%s', but got: %v", tc.errorContains, err) + } + testutil.Assert(t, result == nil, "Expected nil result on error, got: %v", result) + } else { + testutil.Ok(t, err) + testutil.Equals(t, tc.expectedResult, result) + + // Verify outer slice (AZs) is sorted alphabetically. + if err == nil && len(result) > 1 { + azOrderCorrect := sort.SliceIsSorted(result, func(i, j int) bool { + return result[i][0].AZ < result[j][0].AZ + }) + testutil.Assert(t, azOrderCorrect, "Outer slice is not sorted by AZ") + } } - } + }) } - return ordinals } diff --git a/pkg/receive/rendezvous_hashring.go b/pkg/receive/rendezvous_hashring.go new file mode 100644 index 00000000000..220ad26f864 --- /dev/null +++ b/pkg/receive/rendezvous_hashring.go @@ -0,0 +1,108 @@ +// Copyright (c) The Thanos Authors. +// Licensed under the Apache License 2.0. + +package receive + +import ( + "encoding/binary" + "fmt" + + "github.com/cespare/xxhash" + "github.com/pkg/errors" + + "github.com/thanos-io/thanos/pkg/store/labelpb" + "github.com/thanos-io/thanos/pkg/store/storepb/prompb" +) + +// rendezvousHashring distributes series across shards using rendezvous (highest random weight) hashing. +// For each series, it computes hash(seriesKey, shard) for every shard and picks the shard with the +// highest hash value. This avoids virtual nodes entirely while providing good distribution. +type rendezvousHashring struct { + azEndpoints [][]Endpoint // [azIndex][shardIndex] → Endpoint + sortedAZs []string + numShards int + replicationFactor uint64 + flatEndpoints []Endpoint +} + +func newRendezvousHashring(endpoints []Endpoint, replicationFactor uint64) (*rendezvousHashring, error) { + if replicationFactor == 0 { + return nil, errors.New("replication factor cannot be zero") + } + + groupedEndpoints, err := groupByAZ(endpoints) + if err != nil { + return nil, errors.Wrap(err, "failed to group endpoints by AZ") + } + + numAZs := len(groupedEndpoints) + if numAZs == 0 { + return nil, errors.New("no endpoint groups found after grouping by AZ") + } + if uint64(numAZs) != replicationFactor { + return nil, fmt.Errorf("number of AZs (%d) must equal replication factor (%d)", numAZs, replicationFactor) + } + + numShards := len(groupedEndpoints[0]) + if numShards == 0 { + return nil, errors.New("AZ groups are empty after grouping") + } + + // Build sorted AZ names from groupedEndpoints (already sorted by groupByAZ). + sortedAZs := make([]string, numAZs) + for i := range groupedEndpoints { + sortedAZs[i] = groupedEndpoints[i][0].AZ + } + + // Build flat endpoint list: all AZ0 endpoints, then AZ1, etc. + flatEndpoints := make([]Endpoint, 0, numAZs*numShards) + for azIdx := 0; azIdx < numAZs; azIdx++ { + flatEndpoints = append(flatEndpoints, groupedEndpoints[azIdx]...) + } + + return &rendezvousHashring{ + azEndpoints: groupedEndpoints, + sortedAZs: sortedAZs, + numShards: numShards, + replicationFactor: replicationFactor, + flatEndpoints: flatEndpoints, + }, nil +} + +func (r *rendezvousHashring) GetN(tenant string, ts *prompb.TimeSeries, n uint64) (Endpoint, error) { + if n >= r.replicationFactor { + return Endpoint{}, &insufficientNodesError{have: r.replicationFactor, want: n + 1} + } + + seriesKey := labelpb.HashWithPrefix(tenant, ts.Labels) + + // Rendezvous hashing: for each shard, compute hash(seriesKey, shard), pick the max. + // buf is a fixed-size stack array: 8 bytes seriesKey + 2 bytes shard (little-endian). + bestShard := 0 + bestHash := uint64(0) + + var buf [10]byte + binary.LittleEndian.PutUint64(buf[:8], seriesKey) + + for shard := 0; shard < r.numShards; shard++ { + binary.LittleEndian.PutUint16(buf[8:], uint16(shard)) + h := xxhash.Sum64(buf[:]) + if shard == 0 || h > bestHash { + bestHash = h + bestShard = shard + } + } + + // n selects the AZ (replica index). + return r.azEndpoints[n][bestShard], nil +} + +func (r *rendezvousHashring) Get(tenant string, ts *prompb.TimeSeries) (Endpoint, error) { + return r.GetN(tenant, ts, 0) +} + +func (r *rendezvousHashring) Nodes() []Endpoint { + return r.flatEndpoints +} + +func (r *rendezvousHashring) Close() {} diff --git a/pkg/receive/rendezvous_hashring_test.go b/pkg/receive/rendezvous_hashring_test.go new file mode 100644 index 00000000000..84f6e37b0df --- /dev/null +++ b/pkg/receive/rendezvous_hashring_test.go @@ -0,0 +1,593 @@ +// Copyright (c) The Thanos Authors. +// Licensed under the Apache License 2.0. + +package receive + +import ( + "fmt" + "math" + "sort" + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + + "github.com/thanos-io/thanos/pkg/store/labelpb" + "github.com/thanos-io/thanos/pkg/store/storepb/prompb" +) + +// makeK8sEndpoint creates an endpoint with K8s-style DNS name. +func makeK8sEndpoint(podName string, shard int, az string) Endpoint { + return Endpoint{ + Address: fmt.Sprintf("%s-%d.svc.test.svc.cluster.local:10901", podName, shard), + AZ: az, + Shard: shard, + } +} + +// extractShardFromAddress extracts shard number from K8s-style address. +func extractShardFromAddress(t *testing.T, address string) int { + t.Helper() + // Address format: pod-az-N.svc.test.svc.cluster.local:10901 + parts := strings.Split(address, ".") + require.Greater(t, len(parts), 0) + podPart := parts[0] // pod-az-N + lastDash := strings.LastIndex(podPart, "-") + require.Greater(t, lastDash, 0) + shardStr := podPart[lastDash+1:] + var shard int + _, err := fmt.Sscanf(shardStr, "%d", &shard) + require.NoError(t, err) + return shard +} + +// extractShardsFromSubring extracts unique shards from a subring. +func extractShardsFromSubring(t *testing.T, subring Hashring) []int { + t.Helper() + nodes := subring.Nodes() + shardSet := make(map[int]struct{}) + for _, node := range nodes { + shardSet[extractShardFromAddress(t, node.Address)] = struct{}{} + } + shards := make([]int, 0, len(shardSet)) + for s := range shardSet { + shards = append(shards, s) + } + sort.Ints(shards) + return shards +} + +func TestRendezvousHashringConstructor(t *testing.T) { + t.Parallel() + + ep0a := Endpoint{Address: podDNS("pod", 0), AZ: "zone-a", Shard: 0} + ep1a := Endpoint{Address: podDNS("pod", 1), AZ: "zone-a", Shard: 1} + ep0b := Endpoint{Address: podDNS("pod", 0), AZ: "zone-b", Shard: 0} + ep1b := Endpoint{Address: podDNS("pod", 1), AZ: "zone-b", Shard: 1} + ep0c := Endpoint{Address: podDNS("pod", 0), AZ: "zone-c", Shard: 0} + ep1c := Endpoint{Address: podDNS("pod", 1), AZ: "zone-c", Shard: 1} + duplicateEp0a := Endpoint{Address: podDNS("anotherpod", 0), AZ: "zone-a", Shard: 0} + + testCases := map[string]struct { + endpoints []Endpoint + replicationFactor uint64 + expectError bool + errorContains string + }{ + "valid 2 AZs, RF=2": { + endpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b}, + replicationFactor: 2, + }, + "valid 3 AZs, RF=3": { + endpoints: []Endpoint{ep1a, ep0b, ep0a, ep1b, ep0c, ep1c}, + replicationFactor: 3, + }, + "error: empty input": { + endpoints: []Endpoint{}, + replicationFactor: 1, + expectError: true, + errorContains: "no endpoints provided", + }, + "error: RF=0": { + endpoints: []Endpoint{ep0a, ep0b}, + replicationFactor: 0, + expectError: true, + errorContains: "replication factor cannot be zero", + }, + "error: duplicate shard": { + endpoints: []Endpoint{ep0a, ep1a, ep0b, duplicateEp0a}, + replicationFactor: 2, + expectError: true, + errorContains: "duplicate endpoint", + }, + "error: missing shard 0": { + endpoints: []Endpoint{ep1a, ep1b}, + replicationFactor: 2, + expectError: true, + errorContains: "missing endpoint with shard 0", + }, + "error: AZ count != RF (too few AZs)": { + endpoints: []Endpoint{ep0a, ep1a}, + replicationFactor: 2, + expectError: true, + errorContains: "number of AZs (1) must equal replication factor (2)", + }, + "error: AZ count != RF (too many AZs)": { + endpoints: []Endpoint{ep0a, ep1a, ep0b, ep1b, ep0c, ep1c}, + replicationFactor: 2, + expectError: true, + errorContains: "number of AZs (3) must equal replication factor (2)", + }, + "unbalanced AZs uses common subset": { + endpoints: []Endpoint{ep0a, ep1a, ep0b}, + replicationFactor: 2, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + ring, err := newRendezvousHashring(tc.endpoints, tc.replicationFactor) + if tc.expectError { + require.Error(t, err) + require.Nil(t, ring) + if tc.errorContains != "" { + require.Contains(t, err.Error(), tc.errorContains) + } + } else { + require.NoError(t, err) + require.NotNil(t, ring) + } + }) + } +} + +func TestRendezvousHashringGetN(t *testing.T) { + t.Parallel() + + ep0a := Endpoint{Address: podDNS("pod", 0), AZ: "zone-a", Shard: 0} + ep1a := Endpoint{Address: podDNS("pod", 1), AZ: "zone-a", Shard: 1} + ep0b := Endpoint{Address: podDNS("pod", 0), AZ: "zone-b", Shard: 0} + ep1b := Endpoint{Address: podDNS("pod", 1), AZ: "zone-b", Shard: 1} + ep0c := Endpoint{Address: podDNS("pod", 0), AZ: "zone-c", Shard: 0} + ep1c := Endpoint{Address: podDNS("pod", 1), AZ: "zone-c", Shard: 1} + + ts := &prompb.TimeSeries{ + Labels: []labelpb.ZLabel{{Name: "test", Value: "replica-routing"}}, + } + + t.Run("2 AZs RF=2, replicas have same shard", func(t *testing.T) { + ring, err := newRendezvousHashring([]Endpoint{ep1a, ep0b, ep0a, ep1b}, 2) + require.NoError(t, err) + + r0, err := ring.GetN("tenant1", ts, 0) + require.NoError(t, err) + r1, err := ring.GetN("tenant1", ts, 1) + require.NoError(t, err) + + require.Equal(t, r0.Shard, r1.Shard, "replicas should have same shard") + require.NotEqual(t, r0.AZ, r1.AZ, "replicas should be in different AZs") + }) + + t.Run("3 AZs RF=3, replicas have same shard", func(t *testing.T) { + ring, err := newRendezvousHashring([]Endpoint{ep1a, ep0b, ep0a, ep1b, ep0c, ep1c}, 3) + require.NoError(t, err) + + r0, err := ring.GetN("tenant1", ts, 0) + require.NoError(t, err) + r1, err := ring.GetN("tenant1", ts, 1) + require.NoError(t, err) + r2, err := ring.GetN("tenant1", ts, 2) + require.NoError(t, err) + + require.Equal(t, r0.Shard, r1.Shard) + require.Equal(t, r1.Shard, r2.Shard) + + azs := map[string]struct{}{r0.AZ: {}, r1.AZ: {}, r2.AZ: {}} + require.Len(t, azs, 3, "replicas should span all 3 AZs") + }) + + t.Run("error: n >= replicationFactor", func(t *testing.T) { + ring, err := newRendezvousHashring([]Endpoint{ep1a, ep0b, ep0a, ep1b}, 2) + require.NoError(t, err) + + _, err = ring.GetN("tenant1", ts, 2) + require.Error(t, err) + require.Contains(t, err.Error(), "insufficient nodes") + }) +} + +func TestRendezvousHashringSameShardAcrossAZs(t *testing.T) { + t.Parallel() + + // Create 3 AZs with 20 shards each. + var endpoints []Endpoint + for i := 0; i < 20; i++ { + endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-a", Shard: i}) + } + for i := 0; i < 20; i++ { + endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-b", Shard: i}) + } + for i := 0; i < 20; i++ { + endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-c", Shard: i}) + } + + ring, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + // For many series, verify all replicas go to the same shard. + for i := 0; i < 500; i++ { + ts := &prompb.TimeSeries{ + Labels: []labelpb.ZLabel{ + {Name: "__name__", Value: "test_metric"}, + {Name: "series", Value: fmt.Sprintf("series-%d", i)}, + }, + } + + r0, err := ring.GetN("tenant1", ts, 0) + require.NoError(t, err) + r1, err := ring.GetN("tenant1", ts, 1) + require.NoError(t, err) + r2, err := ring.GetN("tenant1", ts, 2) + require.NoError(t, err) + + require.Equal(t, r0.Shard, r1.Shard, "series %d: shard mismatch between AZ0 and AZ1", i) + require.Equal(t, r1.Shard, r2.Shard, "series %d: shard mismatch between AZ1 and AZ2", i) + + azs := map[string]struct{}{r0.AZ: {}, r1.AZ: {}, r2.AZ: {}} + require.Len(t, azs, 3, "series %d: replicas should span all 3 AZs", i) + } +} + +func TestRendezvousHashringDistribution(t *testing.T) { + t.Parallel() + + numShards := 10 + var endpoints []Endpoint + for i := 0; i < numShards; i++ { + endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-a", Shard: i}) + endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-b", Shard: i}) + endpoints = append(endpoints, Endpoint{Address: podDNS("pod", i), AZ: "zone-c", Shard: i}) + } + + ring, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + shardCounts := make(map[int]int) + numSeries := 10000 + for i := 0; i < numSeries; i++ { + ts := &prompb.TimeSeries{ + Labels: []labelpb.ZLabel{ + {Name: "__name__", Value: "test_metric"}, + {Name: "instance", Value: fmt.Sprintf("instance-%d", i)}, + }, + } + + ep, err := ring.Get("tenant1", ts) + require.NoError(t, err) + shardCounts[ep.Shard]++ + } + + // Expect all shards to receive some series. + require.Len(t, shardCounts, numShards, "all shards should receive some series") + + // Check uniformity: each shard should get roughly numSeries/numShards. + expected := float64(numSeries) / float64(numShards) + for shard, count := range shardCounts { + deviation := math.Abs(float64(count)-expected) / expected + require.Less(t, deviation, 0.30, + "shard %d has %d series (expected ~%.0f), deviation %.1f%%", + shard, count, expected, deviation*100) + } +} + +func TestRendezvousShuffleShardingBasic(t *testing.T) { + t.Parallel() + + // Create 3 AZs with 5 shards each. + endpoints := make([]Endpoint, 0, 15) + azs := []string{"az-a", "az-b", "az-c"} + for _, az := range azs { + for ord := 0; ord < 5; ord++ { + endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) + } + } + + baseRing, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + cfg := ShuffleShardingConfig{ + ShardSize: 6, + } + shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-rendezvous") + require.NoError(t, err) + + tenant := "test-tenant" + shard, err := shardRing.getTenantShardRendezvous(tenant) + require.NoError(t, err) + + // Verify we got the right number of nodes (6 total / 3 AZs = 2 shards per AZ → 6 endpoints). + nodes := shard.Nodes() + require.Len(t, nodes, 6, "expected 6 endpoints (2 shards * 3 AZs)") + + // Extract shards from each AZ and verify they're the same. + shardsByAZ := make(map[string][]int) + for _, node := range nodes { + shardsByAZ[node.AZ] = append(shardsByAZ[node.AZ], extractShardFromAddress(t, node.Address)) + } + + require.Len(t, shardsByAZ, 3, "expected 3 AZs") + for az, shards := range shardsByAZ { + require.Len(t, shards, 2, "AZ %s should have 2 shards", az) + } + + // All AZs should have the same shards. + var referenceShards []int + for _, shards := range shardsByAZ { + if referenceShards == nil { + referenceShards = shards + } else { + require.ElementsMatch(t, referenceShards, shards, + "all AZs should have the same shards") + } + } +} + +func TestRendezvousShuffleShardingConsistency(t *testing.T) { + t.Parallel() + + endpoints := make([]Endpoint, 0, 15) + azs := []string{"az-a", "az-b", "az-c"} + for _, az := range azs { + for ord := 0; ord < 5; ord++ { + endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) + } + } + + baseRing, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + cfg := ShuffleShardingConfig{ + ShardSize: 6, + } + shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-consistency") + require.NoError(t, err) + + tenant := "consistent-tenant" + var firstShards []int + + for trial := 0; trial < 10; trial++ { + shard, err := shardRing.getTenantShardRendezvous(tenant) + require.NoError(t, err) + currentShards := extractShardsFromSubring(t, shard) + if firstShards == nil { + firstShards = currentShards + } else { + require.Equal(t, firstShards, currentShards, + "same tenant should always get same shards") + } + } +} + +func TestRendezvousShuffleShardingDifferentTenants(t *testing.T) { + t.Parallel() + + endpoints := make([]Endpoint, 0, 30) + azs := []string{"az-a", "az-b", "az-c"} + for _, az := range azs { + for ord := 0; ord < 10; ord++ { + endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) + } + } + + baseRing, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + cfg := ShuffleShardingConfig{ + ShardSize: 9, + } + shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-diff-tenants") + require.NoError(t, err) + + tenantShards := make(map[string][]int) + numTenants := 20 + + for i := 0; i < numTenants; i++ { + tenant := fmt.Sprintf("tenant-%d", i) + shard, err := shardRing.getTenantShardRendezvous(tenant) + require.NoError(t, err) + tenantShards[tenant] = extractShardsFromSubring(t, shard) + } + + uniqueSets := make(map[string]int) + for _, shards := range tenantShards { + key := fmt.Sprintf("%v", shards) + uniqueSets[key]++ + } + + require.Greater(t, len(uniqueSets), 1, "different tenants should get different shard sets") +} + +func TestRendezvousShuffleShardingPreservesAlignment(t *testing.T) { + t.Parallel() + + endpoints := make([]Endpoint, 0, 15) + azs := []string{"az-a", "az-b", "az-c"} + for _, az := range azs { + for ord := 0; ord < 5; ord++ { + endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) + } + } + + baseRing, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + cfg := ShuffleShardingConfig{ + ShardSize: 6, + } + shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-preserves") + require.NoError(t, err) + + tenant := "alignment-test-tenant" + + for i := 0; i < 100; i++ { + ts := &prompb.TimeSeries{ + Labels: []labelpb.ZLabel{ + {Name: "series", Value: fmt.Sprintf("series-%d", i)}, + }, + } + + var replicas []Endpoint + for n := uint64(0); n < 3; n++ { + ep, err := shardRing.GetN(tenant, ts, n) + require.NoError(t, err) + replicas = append(replicas, ep) + } + + // All replicas should have the same shard but different AZs. + shardsSeen := make(map[int]struct{}) + azsSeen := make(map[string]struct{}) + for _, ep := range replicas { + shardsSeen[extractShardFromAddress(t, ep.Address)] = struct{}{} + azsSeen[ep.AZ] = struct{}{} + } + + require.Len(t, shardsSeen, 1, "all replicas should have the same shard for series %d", i) + require.Len(t, azsSeen, 3, "replicas should span all 3 AZs for series %d", i) + } +} + +func TestRendezvousShuffleShardingDataDistribution(t *testing.T) { + t.Parallel() + + endpoints := make([]Endpoint, 0, 15) + azs := []string{"az-a", "az-b", "az-c"} + for _, az := range azs { + for ord := 0; ord < 5; ord++ { + endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) + } + } + + baseRing, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + cfg := ShuffleShardingConfig{ + ShardSize: 6, + } + shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-distribution") + require.NoError(t, err) + + tenant := "distribution-test-tenant" + + shard, err := shardRing.getTenantShardRendezvous(tenant) + require.NoError(t, err) + selectedShards := extractShardsFromSubring(t, shard) + require.Len(t, selectedShards, 2, "tenant should have exactly 2 shards") + + seriesByShard := make(map[int]map[int]struct{}) + for _, ord := range selectedShards { + seriesByShard[ord] = make(map[int]struct{}) + } + + seriesByEndpoint := make(map[string]map[int]struct{}) + + numSeries := 1000 + for i := 0; i < numSeries; i++ { + ts := &prompb.TimeSeries{ + Labels: []labelpb.ZLabel{ + {Name: "series", Value: fmt.Sprintf("series-%d", i)}, + {Name: "__name__", Value: "test_metric"}, + }, + } + + var replicas []Endpoint + for n := uint64(0); n < 3; n++ { + ep, err := shardRing.GetN(tenant, ts, n) + require.NoError(t, err) + replicas = append(replicas, ep) + + if seriesByEndpoint[ep.Address] == nil { + seriesByEndpoint[ep.Address] = make(map[int]struct{}) + } + seriesByEndpoint[ep.Address][i] = struct{}{} + } + + // All replicas should have the same shard. + primaryShard := extractShardFromAddress(t, replicas[0].Address) + for _, ep := range replicas[1:] { + epShard := extractShardFromAddress(t, ep.Address) + require.Equal(t, primaryShard, epShard, "all replicas for series %d should have same shard", i) + } + + seriesByShard[primaryShard][i] = struct{}{} + } + + // Both shards should receive series. + for ord, series := range seriesByShard { + require.Greater(t, len(series), 0, "shard %d should receive some series", ord) + } + + // Same shard across different AZs should receive the same series. + endpointsByShard := make(map[int][]string) + for addr := range seriesByEndpoint { + ord := extractShardFromAddress(t, addr) + endpointsByShard[ord] = append(endpointsByShard[ord], addr) + } + + for ord, addrs := range endpointsByShard { + if len(addrs) < 2 { + continue + } + referenceSeries := seriesByEndpoint[addrs[0]] + for _, addr := range addrs[1:] { + otherSeries := seriesByEndpoint[addr] + require.Equal(t, len(referenceSeries), len(otherSeries), + "endpoints with shard %d should have same number of series", ord) + for seriesIdx := range referenceSeries { + _, ok := otherSeries[seriesIdx] + require.True(t, ok, "series %d should be on all endpoints with shard %d", seriesIdx, ord) + } + } + } + + // Different shards should receive different series. + shardList := make([]int, 0, len(seriesByShard)) + for ord := range seriesByShard { + shardList = append(shardList, ord) + } + if len(shardList) >= 2 { + series1 := seriesByShard[shardList[0]] + series2 := seriesByShard[shardList[1]] + for seriesIdx := range series1 { + _, overlap := series2[seriesIdx] + require.False(t, overlap, + "series %d should not be on both shard %d and shard %d", + seriesIdx, shardList[0], shardList[1]) + } + } +} + +func TestRendezvousShuffleShardingValidation(t *testing.T) { + t.Parallel() + + endpoints := make([]Endpoint, 0, 15) + azs := []string{"az-a", "az-b", "az-c"} + for _, az := range azs { + for ord := 0; ord < 5; ord++ { + endpoints = append(endpoints, makeK8sEndpoint("pod-"+az, ord, az)) + } + } + + baseRing, err := newRendezvousHashring(endpoints, 3) + require.NoError(t, err) + + cfg := ShuffleShardingConfig{ + ShardSize: 30, // 30 / 3 AZs = 10 per-AZ, but only 5 shards available + } + shardRing, err := newShuffleShardHashring(baseRing, cfg, 3, prometheus.NewRegistry(), "test-validation") + require.NoError(t, err) + + _, err = shardRing.getTenantShardRendezvous("test-tenant") + require.Error(t, err) + require.Contains(t, err.Error(), "exceeds available common shards") +} diff --git a/pkg/store/labelpb/label.go b/pkg/store/labelpb/label.go index 90192d0fac8..fcaeb81e954 100644 --- a/pkg/store/labelpb/label.go +++ b/pkg/store/labelpb/label.go @@ -12,6 +12,7 @@ import ( "io" "sort" "strings" + "sync" "unsafe" "github.com/cespare/xxhash/v2" @@ -350,10 +351,21 @@ func DeepCopy(lbls []ZLabel) []ZLabel { return ret } +var hashBufPool = sync.Pool{ + New: func() any { + b := make([]byte, 0, 1024) + return &b + }, +} + +const maxHashBufPoolCap = 4096 + // HashWithPrefix returns a hash for the given prefix and labels. func HashWithPrefix(prefix string, lbls []ZLabel) uint64 { // Use xxhash.Sum64(b) for fast path as it's faster. - b := make([]byte, 0, 1024) + bp := hashBufPool.Get().(*[]byte) + b := (*bp)[:0] + b = append(b, prefix...) b = append(b, sep[0]) @@ -368,6 +380,10 @@ func HashWithPrefix(prefix string, lbls []ZLabel) uint64 { _, _ = h.WriteString(v.Value) _, _ = h.Write(sep) } + if cap(b) <= maxHashBufPoolCap { + *bp = b + hashBufPool.Put(bp) + } return h.Sum64() } b = append(b, v.Name...) @@ -375,7 +391,12 @@ func HashWithPrefix(prefix string, lbls []ZLabel) uint64 { b = append(b, v.Value...) b = append(b, sep[0]) } - return xxhash.Sum64(b) + result := xxhash.Sum64(b) + if cap(b) <= maxHashBufPoolCap { + *bp = b + hashBufPool.Put(bp) + } + return result } // ValidateLabels validates label names and values (checks for empty From af73653b4f07bb5a15fa160611f590172d88704d Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 19:18:19 +0000 Subject: [PATCH 2/9] =?UTF-8?q?Upgrade=20golangci-lint=20v1.61.0=20?= =?UTF-8?q?=E2=86=92=20v2.11.1=20and=20fix=20all=20lint=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Migrate .golangci.yml from v1 to v2 config format - Replace deprecated exportloopref with copyloopvar; remove 16 unnecessary loop variable copies - Fix 39+ auto-fixable lint issues (QF1003, QF1007, QF1008, QF1011, QF1012, ST1023, QF1001) - Fix duplicate streamer import (ST1019), error var naming (ST1012), error string formatting (ST1005) - Rename defaultMaxResolutionSeconds → defaultMaxResolution (ST1011) - Remove malformed json tag on embedded TSDBStatus field (SA5008) - Simplify De Morgan's expression in fetcher_test.go (QF1001) - Update bingo to install golangci-lint v2.11.1 - Add .vscode/ to .gitignore Co-Authored-By: Claude Opus 4.6 --- .bingo/Variables.mk | 6 +- .bingo/golangci-lint.mod | 2 +- .gitignore | 1 + .golangci.yml | 137 +++++++++--------- cmd/thanos/query_frontend.go | 10 +- cmd/thanos/streamer_test.go | 7 +- cmd/thanos/tools_streamer.go | 4 +- pkg/api/blocks/v1_test.go | 5 +- pkg/api/query/grpc.go | 36 ++--- pkg/api/query/v1_test.go | 5 +- pkg/api/status/v1.go | 4 +- pkg/block/fetcher.go | 2 +- pkg/block/fetcher_test.go | 5 +- pkg/cacheutil/redis_client.go | 5 +- pkg/compact/compact.go | 22 +-- pkg/dedup/chunk_iter_test.go | 2 - pkg/dedup/iter_test.go | 1 - pkg/discovery/memcache/resolver.go | 2 +- pkg/errors/errors.go | 4 +- pkg/errors/stacktrace.go | 2 +- pkg/extkingpin/path_content_reloader_test.go | 1 - pkg/query/endpointset.go | 2 - pkg/query/endpointset_test.go | 12 +- pkg/query/remote_engine.go | 2 +- pkg/queryfrontend/config.go | 14 +- pkg/querysharding/analyzer.go | 11 +- pkg/receive/handler.go | 4 +- pkg/receive/hashring.go | 7 +- pkg/receive/multitsdb.go | 1 - pkg/receive/multitsdb_test.go | 2 - pkg/replicate/scheme.go | 8 +- pkg/replicate/scheme_test.go | 2 +- pkg/shipper/shipper.go | 2 +- pkg/shipper/shipper_e2e_test.go | 10 +- pkg/shipper/shipper_test.go | 2 +- pkg/store/bucket.go | 7 +- pkg/store/bucket_test.go | 12 +- pkg/store/cache/caching_bucket.go | 1 - pkg/store/postings.go | 5 +- pkg/store/proxy.go | 11 +- pkg/store/proxy_merge.go | 8 +- pkg/testutil/e2eutil/prometheus.go | 7 +- .../tracing_middleware/interceptors_test.go | 9 +- test/e2e/e2ethanos/services.go | 10 +- test/e2e/query_frontend_test.go | 1 - test/e2e/query_test.go | 2 +- 46 files changed, 188 insertions(+), 227 deletions(-) diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 6227d0c6bf4..4b5cbc2bb6e 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -53,11 +53,11 @@ $(GOJSONTOYAML): $(BINGO_DIR)/gojsontoyaml.mod @echo "(re)installing $(GOBIN)/gojsontoyaml-v0.1.0" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=gojsontoyaml.mod -o=$(GOBIN)/gojsontoyaml-v0.1.0 "github.com/brancz/gojsontoyaml" -GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.61.0 +GOLANGCI_LINT := $(GOBIN)/golangci-lint-v2.11.1 $(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/golangci-lint-v1.61.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.61.0 "github.com/golangci/golangci-lint/cmd/golangci-lint" + @echo "(re)installing $(GOBIN)/golangci-lint-v2.11.1" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v2.11.1 "github.com/golangci/golangci-lint/v2/cmd/golangci-lint" GOTESPLIT := $(GOBIN)/gotesplit-v0.2.1 $(GOTESPLIT): $(BINGO_DIR)/gotesplit.mod diff --git a/.bingo/golangci-lint.mod b/.bingo/golangci-lint.mod index fd88f3233f2..050d7aa0b08 100644 --- a/.bingo/golangci-lint.mod +++ b/.bingo/golangci-lint.mod @@ -4,4 +4,4 @@ go 1.22.1 toolchain go1.23.1 -require github.com/golangci/golangci-lint v1.61.0 // cmd/golangci-lint +require github.com/golangci/golangci-lint/v2 v2.11.1 // cmd/golangci-lint diff --git a/.gitignore b/.gitignore index 139d9936c52..197e8e1a70a 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ examples/tmp/ # Ignore generated Cortex files internal/cortex/querier/active-query-tracker +.vscode/ diff --git a/.golangci.yml b/.golangci.yml index ce3b2182442..9293597f904 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,82 +1,77 @@ -# This file contains all available configuration options -# with their default values. - -# options for analysis running +version: "2" run: - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 5m - - # exit code when at least one issue was found, default is 1 issues-exit-code: 1 - -# output configuration options output: - # The formats used to render issues. formats: - - format: colored-line-number + text: path: stdout - - # print lines of code with issue, default is true - print-issued-lines: true - - # print linter name in the end of issue text, default is true - print-linter-name: true - + print-linter-name: true + print-issued-lines: true linters: enable: - # Sorted alphabetically. - - errcheck + - copyloopvar - goconst - godot - - gofmt - - goimports - - gosimple - - govet - - ineffassign - misspell - - staticcheck - - typecheck - - unparam - - unused - - exportloopref - promlinter - -linters-settings: - errcheck: - # List of functions to exclude from checking, where each entry is a single function to exclude. - exclude-functions: - - (github.com/go-kit/log.Logger).Log - - fmt.Fprintln - - fmt.Fprint - misspell: - locale: US - goconst: - min-occurrences: 5 - -issues: - exclude-rules: - # We don't check metrics naming in the tests. - - path: _test\.go - linters: - - promlinter - # These are not being checked since these methods exist - # so that no one else could implement them. - - linters: - - unused - text: "SourceStoreAPI.implementsStoreAPI" - - linters: - - unused - text: "SourceStoreAPI.producesBlocks" - - linters: - - unused - text: "Source.producesBlocks" - - linters: - - unused - text: "newMockAlertmanager" - - linters: - - unused - text: "ruleAndAssert" - # Which dirs to exclude: issues from them won't be reported. - exclude-dirs: - - vendor - - internal/cortex + - unparam + settings: + errcheck: + exclude-functions: + - (github.com/go-kit/log.Logger).Log + - fmt.Fprintln + - fmt.Fprint + goconst: + min-occurrences: 5 + misspell: + locale: US + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + # We don't check metrics naming in the tests. + - linters: + - promlinter + path: _test\.go + # These are not being checked since these methods exist + # so that no one else could implement them. + - linters: + - unused + text: SourceStoreAPI.implementsStoreAPI + - linters: + - unused + text: SourceStoreAPI.producesBlocks + - linters: + - unused + text: Source.producesBlocks + - linters: + - unused + text: newMockAlertmanager + - linters: + - unused + text: ruleAndAssert + - linters: + - unparam + path: test/e2e/compatibility_test\.go + paths: + - vendor + - internal/cortex + - third_party$ + - builtin$ + - examples +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - vendor + - internal/cortex + - third_party$ + - builtin$ + - examples diff --git a/cmd/thanos/query_frontend.go b/cmd/thanos/query_frontend.go index 740b647485a..91fa8ab48cf 100644 --- a/cmd/thanos/query_frontend.go +++ b/cmd/thanos/query_frontend.go @@ -73,10 +73,10 @@ func registerQueryFrontend(app *extkingpin.App) { // Query range tripperware flags. cmd.Flag("query-range.align-range-with-step", "Mutate incoming queries to align their start and end with their step for better cache-ability. Note: Grafana dashboards do that by default."). - Default("true").BoolVar(&cfg.QueryRangeConfig.AlignRangeWithStep) + Default("true").BoolVar(&cfg.AlignRangeWithStep) cmd.Flag("query-range.request-downsampled", "Make additional query for downsampled data in case of empty or incomplete response to range request."). - Default("true").BoolVar(&cfg.QueryRangeConfig.RequestDownsampled) + Default("true").BoolVar(&cfg.RequestDownsampled) cmd.Flag("query-range.split-interval", "Split query range requests by an interval and execute in parallel, it should be greater than 0 when query-range.response-cache-config is configured."). Default("24h").DurationVar(&cfg.QueryRangeConfig.SplitQueriesByInterval) @@ -84,13 +84,13 @@ func registerQueryFrontend(app *extkingpin.App) { cmd.Flag("query-range.min-split-interval", "Split query range requests above this interval in query-range.horizontal-shards requests of equal range. "+ "Using this parameter is not allowed with query-range.split-interval. "+ "One should also set query-range.split-min-horizontal-shards to a value greater than 1 to enable splitting."). - Default("0").DurationVar(&cfg.QueryRangeConfig.MinQuerySplitInterval) + Default("0").DurationVar(&cfg.MinQuerySplitInterval) cmd.Flag("query-range.max-split-interval", "Split query range below this interval in query-range.horizontal-shards. Queries with a range longer than this value will be split in multiple requests of this length."). - Default("0").DurationVar(&cfg.QueryRangeConfig.MaxQuerySplitInterval) + Default("0").DurationVar(&cfg.MaxQuerySplitInterval) cmd.Flag("query-range.horizontal-shards", "Split queries in this many requests when query duration is below query-range.max-split-interval."). - Default("0").Int64Var(&cfg.QueryRangeConfig.HorizontalShards) + Default("0").Int64Var(&cfg.HorizontalShards) cmd.Flag("query-range.max-retries-per-request", "Maximum number of retries for a single query range request; beyond this, the downstream error is returned."). Default("5").IntVar(&cfg.QueryRangeConfig.MaxRetries) diff --git a/cmd/thanos/streamer_test.go b/cmd/thanos/streamer_test.go index cc26c177173..fc988e7036a 100644 --- a/cmd/thanos/streamer_test.go +++ b/cmd/thanos/streamer_test.go @@ -18,19 +18,18 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/thanos-io/thanos/pkg/store/storepb" - "github.com/thanos-io/thanos/pkg/streamer" streamer_pkg "github.com/thanos-io/thanos/pkg/streamer" "google.golang.org/grpc" ) func TestConvertToSeriesReq(t *testing.T) { - streamerReq := &streamer.StreamerRequest{ + streamerReq := &streamer_pkg.StreamerRequest{ RequestId: "test_request", StartTimestampMs: 1672531200000, EndTimestampMs: 1672534800000, SkipChunks: false, - LabelMatchers: []streamer.LabelMatcher{ - {Name: "instance", Value: "localhost:9090", Type: streamer.LabelMatcher_EQ}, + LabelMatchers: []streamer_pkg.LabelMatcher{ + {Name: "instance", Value: "localhost:9090", Type: streamer_pkg.LabelMatcher_EQ}, }, Metric: "cpu_usage", } diff --git a/cmd/thanos/tools_streamer.go b/cmd/thanos/tools_streamer.go index bc341b56686..9ac7952e8c3 100644 --- a/cmd/thanos/tools_streamer.go +++ b/cmd/thanos/tools_streamer.go @@ -17,8 +17,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/prometheus/model/labels" "github.com/thanos-io/thanos/pkg/extkingpin" - "github.com/thanos-io/thanos/pkg/streamer" - streamer_pkg "github.com/thanos-io/thanos/pkg/streamer" ) @@ -71,7 +69,7 @@ func runStreamerTool(conf *streamerToolConfig, logger log.Logger) error { nowMs := time.Now().Unix() * 1000 startMs := nowMs - int64(conf.hoursAgo)*3600*1000 - request := &streamer.StreamerRequest{ + request := &streamer_pkg.StreamerRequest{ RequestId: conf.metric, StartTimestampMs: startMs, EndTimestampMs: startMs + 2*3600*1000, diff --git a/pkg/api/blocks/v1_test.go b/pkg/api/blocks/v1_test.go index 49e7ef0681e..2450443f335 100644 --- a/pkg/api/blocks/v1_test.go +++ b/pkg/api/blocks/v1_test.go @@ -56,9 +56,10 @@ func testEndpoint(t *testing.T, test endpointTestCase, name string, responseComp params := test.query.Encode() var body io.Reader - if test.method == http.MethodPost { + switch test.method { + case http.MethodPost: body = strings.NewReader(params) - } else if test.method == "" { + case "": test.method = "ANY" reqURL += "?" + params } diff --git a/pkg/api/query/grpc.go b/pkg/api/query/grpc.go index 093457dd062..3b34b4072dd 100644 --- a/pkg/api/query/grpc.go +++ b/pkg/api/query/grpc.go @@ -23,13 +23,13 @@ import ( ) type GRPCAPI struct { - now func() time.Time - replicaLabels []string - queryableCreate query.QueryableCreator - engineFactory *QueryEngineFactory - defaultEngine querypb.EngineType - lookbackDeltaCreate func(int64) time.Duration - defaultMaxResolutionSeconds time.Duration + now func() time.Time + replicaLabels []string + queryableCreate query.QueryableCreator + engineFactory *QueryEngineFactory + defaultEngine querypb.EngineType + lookbackDeltaCreate func(int64) time.Duration + defaultMaxResolution time.Duration } func NewGRPCAPI( @@ -39,16 +39,16 @@ func NewGRPCAPI( engineFactory *QueryEngineFactory, defaultEngine querypb.EngineType, lookbackDeltaCreate func(int64) time.Duration, - defaultMaxResolutionSeconds time.Duration, + defaultMaxResolution time.Duration, ) *GRPCAPI { return &GRPCAPI{ - now: now, - replicaLabels: replicaLabels, - queryableCreate: creator, - engineFactory: engineFactory, - defaultEngine: defaultEngine, - lookbackDeltaCreate: lookbackDeltaCreate, - defaultMaxResolutionSeconds: defaultMaxResolutionSeconds, + now: now, + replicaLabels: replicaLabels, + queryableCreate: creator, + engineFactory: engineFactory, + defaultEngine: defaultEngine, + lookbackDeltaCreate: lookbackDeltaCreate, + defaultMaxResolution: defaultMaxResolution, } } @@ -70,7 +70,7 @@ func (g *GRPCAPI) Query(request *querypb.QueryRequest, server querypb.Query_Quer maxResolution := request.MaxResolutionSeconds if request.MaxResolutionSeconds == 0 { - maxResolution = g.defaultMaxResolutionSeconds.Milliseconds() / 1000 + maxResolution = g.defaultMaxResolution.Milliseconds() / 1000 } storeMatchers, err := querypb.StoreMatchersToLabelMatchers(request.StoreMatchers) @@ -190,7 +190,7 @@ func (g *GRPCAPI) QueryRange(request *querypb.QueryRangeRequest, srv querypb.Que maxResolution := request.MaxResolutionSeconds if request.MaxResolutionSeconds == 0 { - maxResolution = g.defaultMaxResolutionSeconds.Milliseconds() / 1000 + maxResolution = g.defaultMaxResolution.Milliseconds() / 1000 } storeMatchers, err := querypb.StoreMatchersToLabelMatchers(request.StoreMatchers) @@ -308,7 +308,7 @@ func (g *GRPCAPI) getRangeQueryForEngine( maxResolution := request.MaxResolutionSeconds if request.MaxResolutionSeconds == 0 { - maxResolution = g.defaultMaxResolutionSeconds.Milliseconds() / 1000 + maxResolution = g.defaultMaxResolution.Milliseconds() / 1000 } lookbackDelta := g.lookbackDeltaCreate(maxResolution * 1000) if request.LookbackDeltaSeconds > 0 { diff --git a/pkg/api/query/v1_test.go b/pkg/api/query/v1_test.go index 0c08a0ebca9..bd306f5c1bb 100644 --- a/pkg/api/query/v1_test.go +++ b/pkg/api/query/v1_test.go @@ -97,9 +97,10 @@ func testEndpoint(t *testing.T, test endpointTestCase, name string, responseComp params := test.query.Encode() var body io.Reader - if test.method == http.MethodPost { + switch test.method { + case http.MethodPost: body = strings.NewReader(params) - } else if test.method == "" { + case "": test.method = "ANY" reqURL += "?" + params } diff --git a/pkg/api/status/v1.go b/pkg/api/status/v1.go index 7bfd360e0da..7061d5d340e 100644 --- a/pkg/api/status/v1.go +++ b/pkg/api/status/v1.go @@ -40,8 +40,8 @@ type TenantStats struct { // TSDBStatus has information of cardinality statistics from postings. // TODO(fpetkovski): replace with upstream struct after dependency update. type TSDBStatus struct { - Tenant string `json:"tenant"` - v1.TSDBStatus `json:","` + Tenant string `json:"tenant"` + v1.TSDBStatus } type GetStatsFunc func(r *http.Request, statsByLabelName string) ([]TenantStats, *api.ApiError) diff --git a/pkg/block/fetcher.go b/pkg/block/fetcher.go index c0122c80f75..3dd18efa16f 100644 --- a/pkg/block/fetcher.go +++ b/pkg/block/fetcher.go @@ -821,7 +821,7 @@ func (f *LabelShardedMetaFilter) Filter(_ context.Context, metas map[ulid.ULID]* for id, m := range metas { b.Reset(labels.EmptyLabels()) b.Set(BlockIDLabel, id.String()) - b.Set(BlockLevelLabel, strconv.Itoa(m.BlockMeta.Compaction.Level)) + b.Set(BlockLevelLabel, strconv.Itoa(m.Compaction.Level)) for k, v := range m.Thanos.Labels { b.Set(k, v) diff --git a/pkg/block/fetcher_test.go b/pkg/block/fetcher_test.go index c9d787547cb..6b1741c53e3 100644 --- a/pkg/block/fetcher_test.go +++ b/pkg/block/fetcher_test.go @@ -1000,10 +1000,7 @@ func TestReplicaLabelRemover_Modify(t *testing.T) { func compareSliceWithMapKeys(tb testing.TB, m map[ulid.ULID]*metadata.Meta, s []ulid.ULID) { _, file, line, _ := runtime.Caller(1) - matching := true - if len(m) != len(s) { - matching = false - } + matching := len(m) == len(s) for _, val := range s { if m[val] == nil { diff --git a/pkg/cacheutil/redis_client.go b/pkg/cacheutil/redis_client.go index 09e664b4dcf..35059f07d4c 100644 --- a/pkg/cacheutil/redis_client.go +++ b/pkg/cacheutil/redis_client.go @@ -198,10 +198,7 @@ func NewRedisClientWithConfig(logger log.Logger, name string, config RedisClient tlsConfig = tlsClientConfig } - clientSideCacheDisabled := false - if config.CacheSize == 0 { - clientSideCacheDisabled = true - } + clientSideCacheDisabled := config.CacheSize == 0 clientOpts := rueidis.ClientOption{ InitAddress: strings.Split(config.Addr, ","), diff --git a/pkg/compact/compact.go b/pkg/compact/compact.go index d60873bb539..c5f9e670fb5 100644 --- a/pkg/compact/compact.go +++ b/pkg/compact/compact.go @@ -163,7 +163,7 @@ func UntilNextDownsampling(m *metadata.Meta) (time.Duration, error) { func (s *Syncer) SyncMetas(ctx context.Context) error { s.metrics.SyncMetas.Inc() defer measureFuncDuration(s.metrics.SyncMetasDuration, time.Now()) - var cancel func() = func() {} + var cancel = func() {} if s.syncMetasTimeout > 0 { ctx, cancel = context.WithTimeout(ctx, s.syncMetasTimeout) } @@ -514,7 +514,7 @@ func (cg *Group) deleteFromGroup(target map[ulid.ULID]struct{}) { defer cg.mtx.Unlock() var newGroupMeta []*metadata.Meta for _, meta := range cg.metasByMinTime { - if _, found := target[meta.BlockMeta.ULID]; !found { + if _, found := target[meta.ULID]; !found { newGroupMeta = append(newGroupMeta, meta) } } @@ -680,7 +680,7 @@ func (ps *CompactionProgressCalculator) ProgressCalculate(ctx context.Context, g metas := make([]*tsdb.BlockMeta, 0, len(plan)) for _, p := range plan { metas = append(metas, &p.BlockMeta) - toRemove[p.BlockMeta.ULID] = struct{}{} + toRemove[p.ULID] = struct{}{} } g.deleteFromGroup(toRemove) @@ -700,12 +700,12 @@ func (ps *CompactionProgressCalculator) ProgressCalculate(ctx context.Context, g groups = tmpGroups } - ps.CompactProgressMetrics.NumberOfCompactionRuns.Set(0) - ps.CompactProgressMetrics.NumberOfCompactionBlocks.Set(0) + ps.NumberOfCompactionRuns.Set(0) + ps.NumberOfCompactionBlocks.Set(0) for key, iters := range groupCompactions { - ps.CompactProgressMetrics.NumberOfCompactionRuns.Add(float64(iters)) - ps.CompactProgressMetrics.NumberOfCompactionBlocks.Add(float64(groupBlocks[key])) + ps.NumberOfCompactionRuns.Add(float64(iters)) + ps.NumberOfCompactionBlocks.Add(float64(groupBlocks[key])) } return nil @@ -819,9 +819,9 @@ func (ds *DownsampleProgressCalculator) ProgressCalculate(ctx context.Context, g } } - ds.DownsampleProgressMetrics.NumberOfBlocksDownsampled.Set(0) + ds.NumberOfBlocksDownsampled.Set(0) for _, blocks := range groupBlocks { - ds.DownsampleProgressMetrics.NumberOfBlocksDownsampled.Add(float64(blocks)) + ds.NumberOfBlocksDownsampled.Add(float64(blocks)) } return nil @@ -889,9 +889,9 @@ func (rs *RetentionProgressCalculator) ProgressCalculate(ctx context.Context, gr } } - rs.RetentionProgressMetrics.NumberOfBlocksToDelete.Set(0) + rs.NumberOfBlocksToDelete.Set(0) for _, blocks := range groupBlocks { - rs.RetentionProgressMetrics.NumberOfBlocksToDelete.Add(float64(blocks)) + rs.NumberOfBlocksToDelete.Add(float64(blocks)) } return nil diff --git a/pkg/dedup/chunk_iter_test.go b/pkg/dedup/chunk_iter_test.go index ea3d9f3ae51..a24469c5811 100644 --- a/pkg/dedup/chunk_iter_test.go +++ b/pkg/dedup/chunk_iter_test.go @@ -132,7 +132,6 @@ func TestDedupChunkSeriesMerger(t *testing.T) { ), }, } { - tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() merged := m(tc.input...) @@ -439,7 +438,6 @@ func TestDedupChunkSeriesMerger_Histogram(t *testing.T) { } for _, tc := range testCases { - tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() m := NewChunkSeriesMerger() diff --git a/pkg/dedup/iter_test.go b/pkg/dedup/iter_test.go index e822e6f6ef3..5fbe00ac611 100644 --- a/pkg/dedup/iter_test.go +++ b/pkg/dedup/iter_test.go @@ -615,7 +615,6 @@ func TestDedupSeriesIterator_NativeHistograms(t *testing.T) { } for i, c := range casesMixed { - c := c t.Run(fmt.Sprintf("mixed-%d", i), func(t *testing.T) { t.Parallel() it := newDedupSeriesIterator( diff --git a/pkg/discovery/memcache/resolver.go b/pkg/discovery/memcache/resolver.go index 4e7406cfba7..d2304e261ce 100644 --- a/pkg/discovery/memcache/resolver.go +++ b/pkg/discovery/memcache/resolver.go @@ -92,7 +92,7 @@ func (s *memcachedAutoDiscovery) parseConfig(reader *bufio.Reader) (*clusterConf } if len(configVersion)+len(nodes) != configSize { - return nil, fmt.Errorf("expected %d in config payload, but got %d instead.", configSize, len(configVersion)+len(nodes)) + return nil, fmt.Errorf("expected %d in config payload, but got %d instead", configSize, len(configVersion)+len(nodes)) } for _, host := range strings.Split(strings.TrimSpace(nodes), " ") { diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 2a56473a12e..0dc87abec68 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -105,10 +105,10 @@ func formatErrorChain(err error) string { var buf strings.Builder for err != nil { if e, ok := err.(*base); ok { - buf.WriteString(fmt.Sprintf("%s\n%v", e.info, e.stack)) + fmt.Fprintf(&buf, "%s\n%v", e.info, e.stack) err = e.err } else { - buf.WriteString(fmt.Sprintf("%s\n", err.Error())) + fmt.Fprintf(&buf, "%s\n", err.Error()) err = nil } } diff --git a/pkg/errors/stacktrace.go b/pkg/errors/stacktrace.go index 7c7b9610c57..9e02cf74aed 100644 --- a/pkg/errors/stacktrace.go +++ b/pkg/errors/stacktrace.go @@ -44,7 +44,7 @@ func (s stacktrace) String() string { frame, more := cf.Next() // used formatting scheme <`>`space><:> for example: // > testing.tRunner /home/go/go1.17.8/src/testing/testing.go:1259 - buf.WriteString(fmt.Sprintf("> %s\t%s:%d\n", frame.Func.Name(), frame.File, frame.Line)) + fmt.Fprintf(&buf, "> %s\t%s:%d\n", frame.Func.Name(), frame.File, frame.Line) if !more { break } diff --git a/pkg/extkingpin/path_content_reloader_test.go b/pkg/extkingpin/path_content_reloader_test.go index e02e6aba561..1efa3094a88 100644 --- a/pkg/extkingpin/path_content_reloader_test.go +++ b/pkg/extkingpin/path_content_reloader_test.go @@ -76,7 +76,6 @@ func TestPathContentReloader(t *testing.T) { }, } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() testFile := path.Join(t.TempDir(), "test") diff --git a/pkg/query/endpointset.go b/pkg/query/endpointset.go index 02c9f096b4d..b555aaed586 100644 --- a/pkg/query/endpointset.go +++ b/pkg/query/endpointset.go @@ -365,8 +365,6 @@ func (e *EndpointSet) Update(ctx context.Context) { ) for _, spec := range e.endpointSpec() { - spec := spec - if er, existingRef := e.endpoints[spec.Addr()]; existingRef { wg.Add(1) go func(spec *GRPCEndpointSpec) { diff --git a/pkg/query/endpointset_test.go b/pkg/query/endpointset_test.go index 04a4033a0d2..61583255d23 100644 --- a/pkg/query/endpointset_test.go +++ b/pkg/query/endpointset_test.go @@ -1625,10 +1625,8 @@ func TestDeadlockLocking(t *testing.T) { deadline := time.Now().Add(3 * time.Second) g.Go(func() error { - for { - if time.Now().After(deadline) { - break - } + for !time.Now().After(deadline) { + mockEndpointRef.update(time.Now, &endpointMetadata{ InfoResponse: &infopb.InfoResponse{}, }, nil) @@ -1637,10 +1635,8 @@ func TestDeadlockLocking(t *testing.T) { }) g.Go(func() error { - for { - if time.Now().After(deadline) { - break - } + for !time.Now().After(deadline) { + mockEndpointRef.HasStoreAPI() mockEndpointRef.HasExemplarsAPI() mockEndpointRef.HasMetricMetadataAPI() diff --git a/pkg/query/remote_engine.go b/pkg/query/remote_engine.go index 77a74c9a6fa..f9d733f2123 100644 --- a/pkg/query/remote_engine.go +++ b/pkg/query/remote_engine.go @@ -281,7 +281,7 @@ func (r *remoteQuery) Exec(ctx context.Context) *promql.Result { r.samplesStats = stats.NewQuerySamples(false) // Instant query. - if r.start == r.end { + if r.start.Equal(r.end) { request := &querypb.QueryRequest{ Query: r.plan.String(), QueryPlan: plan, diff --git a/pkg/queryfrontend/config.go b/pkg/queryfrontend/config.go index 437f0cf9adf..e21c0180c3c 100644 --- a/pkg/queryfrontend/config.go +++ b/pkg/queryfrontend/config.go @@ -279,7 +279,7 @@ func (cfg *Config) Validate() error { } } - if cfg.LabelsConfig.DefaultTimeRange == 0 { + if cfg.DefaultTimeRange == 0 { return errors.New("labels.default-time-range cannot be set to 0") } @@ -291,15 +291,15 @@ func (cfg *Config) Validate() error { } func (cfg *Config) validateDynamicSplitParams() error { - if cfg.QueryRangeConfig.HorizontalShards <= 0 { + if cfg.HorizontalShards <= 0 { return errors.New("min horizontal shards should be greater than 0 when query split threshold is enabled") } - if cfg.QueryRangeConfig.MaxQuerySplitInterval <= 0 { + if cfg.MaxQuerySplitInterval <= 0 { return errors.New("max query split interval should be greater than 0 when query split threshold is enabled") } - if cfg.QueryRangeConfig.MinQuerySplitInterval <= 0 { + if cfg.MinQuerySplitInterval <= 0 { return errors.New("min query split interval should be greater than 0 when query split threshold is enabled") } return nil @@ -310,7 +310,7 @@ func (cfg *Config) isStaticSplitSet() bool { } func (cfg *Config) isDynamicSplitSet() bool { - return cfg.QueryRangeConfig.MinQuerySplitInterval > 0 || - cfg.QueryRangeConfig.HorizontalShards > 0 || - cfg.QueryRangeConfig.MaxQuerySplitInterval > 0 + return cfg.MinQuerySplitInterval > 0 || + cfg.HorizontalShards > 0 || + cfg.MaxQuerySplitInterval > 0 } diff --git a/pkg/querysharding/analyzer.go b/pkg/querysharding/analyzer.go index 80cb8cb3a8a..c7a1561b0db 100644 --- a/pkg/querysharding/analyzer.go +++ b/pkg/querysharding/analyzer.go @@ -27,7 +27,7 @@ import ( ) var ( - notShardableErr = fmt.Errorf("expressions are not shardable") + errNotShardable = fmt.Errorf("expressions are not shardable") ) type Analyzer interface { @@ -104,13 +104,14 @@ func (a *QueryAnalyzer) Analyze(query string) (QueryAnalysis, error) { switch n := node.(type) { case *parser.Call: if n.Func != nil { - if n.Func.Name == "label_join" || n.Func.Name == "label_replace" { + switch n.Func.Name { + case "label_join", "label_replace": dstLabel := stringFromArg(n.Args[1]) dynamicLabels = append(dynamicLabels, dstLabel) - } else if n.Func.Name == "absent_over_time" || n.Func.Name == "absent" || n.Func.Name == "scalar" { + case "absent_over_time", "absent", "scalar": isShardable = false - return notShardableErr - } else if n.Func.Name == "histogram_quantile" { + return errNotShardable + case "histogram_quantile": analysis = analysis.scopeToLabels([]string{"le"}, false) } } diff --git a/pkg/receive/handler.go b/pkg/receive/handler.go index 7343c926404..64f958ea5a8 100644 --- a/pkg/receive/handler.go +++ b/pkg/receive/handler.go @@ -979,7 +979,7 @@ type remoteWriteParams struct { } func (h *Handler) gatherWriteStats(rf int, writes ...map[endpointReplica]map[string]trackedSeries) tenantRequestStats { - var stats tenantRequestStats = make(tenantRequestStats) + var stats = make(tenantRequestStats) for _, write := range writes { for er := range write { @@ -1019,7 +1019,7 @@ func (h *Handler) fanoutForward(ctx context.Context, params remoteWriteParams) ( ctx, cancel := context.WithTimeout(tracing.CopyTraceContext(context.Background(), ctx), h.options.ForwardTimeout) var writeErrors writeErrors - var stats tenantRequestStats = make(tenantRequestStats) + var stats = make(tenantRequestStats) defer func() { if writeErrors.ErrOrNil() != nil { diff --git a/pkg/receive/hashring.go b/pkg/receive/hashring.go index dd26e3a50e0..90a9ea1074a 100644 --- a/pkg/receive/hashring.go +++ b/pkg/receive/hashring.go @@ -471,7 +471,7 @@ func newShuffleShardHashring(baseRing Hashring, shuffleShardingConfig ShuffleSha nodeCountByAZ := make(map[string]int) for _, node := range ssh.nodes { - var az string = node.AZ + var az = node.AZ if shuffleShardingConfig.ZoneAwarenessDisabled { az = "" } @@ -530,13 +530,14 @@ func (s *shuffleShardHashring) dedupedNodes() []Endpoint { // getShardSize returns the shard size for a specific tenant, taking into account any overrides. func (s *shuffleShardHashring) getShardSize(tenant string) int { for _, override := range s.shuffleShardingConfig.Overrides { - if override.TenantMatcherType == TenantMatcherTypeExact { + switch override.TenantMatcherType { + case TenantMatcherTypeExact: for _, t := range override.Tenants { if t == tenant { return override.ShardSize } } - } else if override.TenantMatcherType == TenantMatcherGlob { + case TenantMatcherGlob: for _, t := range override.Tenants { matches, err := filepath.Match(t, tenant) if err == nil && matches { diff --git a/pkg/receive/multitsdb.go b/pkg/receive/multitsdb.go index 8de78754abf..7670e7987b9 100644 --- a/pkg/receive/multitsdb.go +++ b/pkg/receive/multitsdb.go @@ -462,7 +462,6 @@ func (t *MultiTSDB) Open() error { var g errgroup.Group for _, f := range files { - f := f if !f.IsDir() { continue } diff --git a/pkg/receive/multitsdb_test.go b/pkg/receive/multitsdb_test.go index 2c46607b6dd..6aa1fc44776 100644 --- a/pkg/receive/multitsdb_test.go +++ b/pkg/receive/multitsdb_test.go @@ -209,8 +209,6 @@ func testMulitTSDBSeries(t *testing.T, m *MultiTSDB) { testutil.Assert(t, len(ss) == 2) for _, s := range ss { - s := s - switch isFoo := strings.Contains(s.String(), "foo"); isFoo { case true: g.Go(func() error { diff --git a/pkg/replicate/scheme.go b/pkg/replicate/scheme.go index 31249fbf771..d362cc6fd7e 100644 --- a/pkg/replicate/scheme.go +++ b/pkg/replicate/scheme.go @@ -96,7 +96,7 @@ func (bf *BlockFilter) Filter(b *metadata.Meta) bool { return false } - gotCompactionLevel := b.BlockMeta.Compaction.Level + gotCompactionLevel := b.Compaction.Level if _, ok := bf.compactionLevels[gotCompactionLevel]; !ok { level.Info(bf.logger).Log("msg", "filtering block", "reason", "compaction level doesn't match allowed levels", "got_compaction_level", gotCompactionLevel, "allowed_compaction_levels", fmt.Sprintf("%v", bf.compactionLevels)) return false @@ -191,12 +191,12 @@ func (rs *replicationScheme) execute(ctx context.Context) error { // In order to prevent races in compactions by the target environment, we // need to replicate oldest start timestamp first. sort.Slice(availableBlocks, func(i, j int) bool { - return availableBlocks[i].BlockMeta.MinTime < availableBlocks[j].BlockMeta.MinTime + return availableBlocks[i].MinTime < availableBlocks[j].MinTime }) for _, b := range availableBlocks { - if err := rs.ensureBlockIsReplicated(ctx, b.BlockMeta.ULID); err != nil { - return errors.Wrapf(err, "ensure block %v is replicated", b.BlockMeta.ULID.String()) + if err := rs.ensureBlockIsReplicated(ctx, b.ULID); err != nil { + return errors.Wrapf(err, "ensure block %v is replicated", b.ULID.String()) } } diff --git a/pkg/replicate/scheme_test.go b/pkg/replicate/scheme_test.go index 6f7f3c4e122..550bd906564 100644 --- a/pkg/replicate/scheme_test.go +++ b/pkg/replicate/scheme_test.go @@ -273,7 +273,7 @@ func TestReplicationSchemeAll(t *testing.T) { prepare: func(ctx context.Context, t *testing.T, originBucket, targetBucket *objstore.InMemBucket) { ulid := testULID(0) meta := testMeta(ulid) - meta.BlockMeta.Compaction.Level = 2 + meta.Compaction.Level = 2 b, err := json.Marshal(meta) testutil.Ok(t, err) diff --git a/pkg/shipper/shipper.go b/pkg/shipper/shipper.go index 6b7be6cd12d..f6b9ca4f859 100644 --- a/pkg/shipper/shipper.go +++ b/pkg/shipper/shipper.go @@ -437,7 +437,7 @@ func (s *Shipper) blockMetasFromOldest() (metas []*metadata.Meta, _ error) { metas = append(metas, m) } sort.Slice(metas, func(i, j int) bool { - return metas[i].BlockMeta.MinTime < metas[j].BlockMeta.MinTime + return metas[i].MinTime < metas[j].MinTime }) return metas, nil } diff --git a/pkg/shipper/shipper_e2e_test.go b/pkg/shipper/shipper_e2e_test.go index ddb963339c5..787e6d679e2 100644 --- a/pkg/shipper/shipper_e2e_test.go +++ b/pkg/shipper/shipper_e2e_test.go @@ -400,14 +400,14 @@ func TestShipper_SyncOverlapBlocks_e2e(t *testing.T) { } } - m[0].BlockMeta.MinTime = 10 - m[0].BlockMeta.MaxTime = 20 + m[0].MinTime = 10 + m[0].MaxTime = 20 - m[1].BlockMeta.MinTime = 15 - m[1].BlockMeta.MaxTime = 17 + m[1].MinTime = 15 + m[1].MaxTime = 17 for i := 0; i < 2; i++ { - bdir := filepath.Join(dir, m[i].BlockMeta.ULID.String()) + bdir := filepath.Join(dir, m[i].ULID.String()) tmp[i] = bdir + ".tmp" metab, err := json.Marshal(&m[i]) diff --git a/pkg/shipper/shipper_test.go b/pkg/shipper/shipper_test.go index 1a5cd5c75ac..43b192aadb6 100644 --- a/pkg/shipper/shipper_test.go +++ b/pkg/shipper/shipper_test.go @@ -126,7 +126,7 @@ func TestIterBlockMetas(t *testing.T) { metas, err := shipper.blockMetasFromOldest() testutil.Ok(t, err) testutil.Equals(t, sort.SliceIsSorted(metas, func(i, j int) bool { - return metas[i].BlockMeta.MinTime < metas[j].BlockMeta.MinTime + return metas[i].MinTime < metas[j].MinTime }), true) } diff --git a/pkg/store/bucket.go b/pkg/store/bucket.go index d8f1b57701e..174b2a468d6 100644 --- a/pkg/store/bucket.go +++ b/pkg/store/bucket.go @@ -1848,7 +1848,6 @@ func (s *BucketStore) LabelNames(ctx context.Context, req *storepb.LabelNamesReq var logger = s.requestLoggerFunc(ctx, s.logger) for _, b := range s.blocks { - b := b gctx := gctx if !b.overlapsClosedInterval(req.Start, req.End) { @@ -2070,8 +2069,6 @@ func (s *BucketStore) LabelValues(ctx context.Context, req *storepb.LabelValuesR var stats = &queryStats{} for _, b := range s.blocks { - b := b - if !b.overlapsClosedInterval(req.Start, req.End) { continue } @@ -2826,7 +2823,6 @@ func checkNilPosting(name, value string, p index.Postings) index.Postings { func matchersToPostingGroups(ctx context.Context, lvalsFn func(name string) ([]string, error), ms []*labels.Matcher) ([]*postingGroup, error) { matchersMap := make(map[string]map[string]*labels.Matcher) for _, m := range ms { - m := m if _, ok := matchersMap[m.Name]; !ok { matchersMap[m.Name] = make(map[string]*labels.Matcher) } @@ -3622,7 +3618,6 @@ func (r *bucketChunkReader) load(ctx context.Context, res []seriesEntry, aggrs [ for _, p := range parts { seq := seq - p := p indices := pIdxs[p.ElemRng[0]:p.ElemRng[1]] g.Go(func() error { return r.loadChunks(ctx, res, aggrs, seq, p, indices, calculateChunkChecksum, bytesLimiter, tenant) @@ -3694,7 +3689,7 @@ func (r *bucketChunkReader) loadChunks(ctx context.Context, res []seriesEntry, a n, err = io.ReadFull(bufReader, cb) readOffset += n // Unexpected EOF for last chunk could be a valid case. Any other errors are definitely real. - if err != nil && !(errors.Is(err, io.ErrUnexpectedEOF) && i == len(pIdxs)-1) { + if err != nil && (!errors.Is(err, io.ErrUnexpectedEOF) || i != len(pIdxs)-1) { return errors.Wrapf(err, "read range for seq %d offset %x", seq, pIdx.offset) } diff --git a/pkg/store/bucket_test.go b/pkg/store/bucket_test.go index f2f923d7264..0581e22bbdc 100644 --- a/pkg/store/bucket_test.go +++ b/pkg/store/bucket_test.go @@ -1559,9 +1559,10 @@ func benchBucketSeries(t testutil.TB, sampleType chunkenc.ValueType, skipChunk, expectedSamples = 1 } seriesCut := int(p * float64(numOfBlocks*seriesPerBlock)) - if seriesCut == 0 { + switch seriesCut { + case 0: seriesCut = 1 - } else if seriesCut == 1 { + case 1: seriesCut = expectedSamples / samplesPerSeriesPerBlock } @@ -3526,7 +3527,7 @@ func TestExpandedPostingsRace(t *testing.T) { testutil.Ok(t, err) m.Thanos = thanosMeta - m.BlockMeta.ULID = ul + m.ULID = ul e2eutil.Copy(t, filepath.Join(tmpDir, blockID.String()), filepath.Join(tmpDir, ul.String())) testutil.Ok(t, m.WriteToDir(log.NewLogfmtLogger(os.Stderr), filepath.Join(tmpDir, ul.String()))) @@ -3561,10 +3562,7 @@ func TestExpandedPostingsRace(t *testing.T) { previousRefs := make(map[int][]storage.SeriesRef) dummyCounter := promauto.With(prometheus.NewRegistry()).NewCounter(prometheus.CounterOpts{Name: "test"}) - for { - if tm.Err() != nil { - break - } + for tm.Err() == nil { m := []*labels.Matcher{ labels.MustNewMatcher(labels.MatchEqual, "foo", "bar"), diff --git a/pkg/store/cache/caching_bucket.go b/pkg/store/cache/caching_bucket.go index 11faa03d6a6..b6cb8587fd1 100644 --- a/pkg/store/cache/caching_bucket.go +++ b/pkg/store/cache/caching_bucket.go @@ -425,7 +425,6 @@ func (cb *CachingBucket) fetchMissingSubranges(ctx context.Context, name string, // Run parallel queries for each missing range. Fetched data is stored into 'hits' map, protected by hitsMutex. g, gctx := errgroup.WithContext(ctx) for _, m := range missing { - m := m g.Go(func() error { r, err := cb.Bucket.GetRange(gctx, name, m.start, m.end-m.start) if err != nil { diff --git a/pkg/store/postings.go b/pkg/store/postings.go index e44dfbe66cb..83453729522 100644 --- a/pkg/store/postings.go +++ b/pkg/store/postings.go @@ -116,10 +116,7 @@ func (r *postingsReaderBuilder) Next() bool { r.keyID = r.postings[r.pi].keyID r.pi++ - for { - if r.pi >= len(r.postings) { - break - } + for r.pi < len(r.postings) { if r.postings[r.pi].ptr.Start == r.postings[r.pi-1].ptr.Start && r.postings[r.pi].ptr.End == r.postings[r.pi-1].ptr.End { diff --git a/pkg/store/proxy.go b/pkg/store/proxy.go index 434cb220e9b..697d0d5ebde 100644 --- a/pkg/store/proxy.go +++ b/pkg/store/proxy.go @@ -236,9 +236,10 @@ func WithBlockedMetricPatterns(patterns []string) ProxyStoreOption { pattern := input[1:] // Exact match patterns ('e') are blocked unconditionally - if typeChar == 'e' { + switch typeChar { + case 'e': s.unconditionalBlockedMetrics[pattern] = struct{}{} - } else if typeChar == 'p' { + case 'p': // Prefix patterns are blocked conditionally (only if insufficient filters) s.blockedMetricPrefixes.Insert(pattern, pattern) } @@ -595,8 +596,6 @@ func (s *ProxyStore) Series(originalRequest *storepb.SeriesRequest, srv storepb. }() for _, st := range stores { - st := st - respSet, err := newAsyncRespSet(ctx, st, r, s.responseTimeout, s.retrievalStrategy, &s.buffers, r.ShardInfo, reqLogger, s.metrics.emptyStreamResponses, s.lazyRetrievalMaxBufferedResponses) if err != nil { // Check if this is a timeout-related error and capture gRPC error code @@ -754,8 +753,6 @@ func (s *ProxyStore) LabelNames(ctx context.Context, originalRequest *storepb.La g, gctx = errgroup.WithContext(ctx) ) for _, st := range stores { - st := st - storeID, storeAddr, isLocalStore := storeInfo(st) g.Go(func() error { span, spanCtx := tracing.StartSpan(gctx, "proxy.label_names", tracing.Tags{ @@ -858,8 +855,6 @@ func (s *ProxyStore) LabelValues(ctx context.Context, originalRequest *storepb.L g, gctx = errgroup.WithContext(ctx) ) for _, st := range stores { - st := st - storeID, storeAddr, isLocalStore := storeInfo(st) g.Go(func() error { span, spanCtx := tracing.StartSpan(gctx, "proxy.label_values", tracing.Tags{ diff --git a/pkg/store/proxy_merge.go b/pkg/store/proxy_merge.go index 54a46692c21..56de8cb5469 100644 --- a/pkg/store/proxy_merge.go +++ b/pkg/store/proxy_merge.go @@ -198,7 +198,7 @@ func (d *responseDeduplicator) At() *storepb.SeriesResponse { // NewProxyResponseLoserTree returns heap that k-way merge series together. // It's agnostic to duplicates and overlaps, it forwards all duplicated series in random order. func NewProxyResponseLoserTree(seriesSets ...respSet) *losertree.Tree[*storepb.SeriesResponse, respSet] { - var maxVal *storepb.SeriesResponse = storepb.NewSeriesResponse(nil) + var maxVal = storepb.NewSeriesResponse(nil) less := func(a, b *storepb.SeriesResponse) bool { if a == maxVal && b != maxVal { @@ -758,10 +758,8 @@ func newEagerRespSet( defer t.Stop() } - for { - if !handleRecvResponse(t) { - break - } + for handleRecvResponse(t) { + } // This should be used only for stores that does not support doing this on server side. diff --git a/pkg/testutil/e2eutil/prometheus.go b/pkg/testutil/e2eutil/prometheus.go index 5d784a9cc0b..2f9a21286e4 100644 --- a/pkg/testutil/e2eutil/prometheus.go +++ b/pkg/testutil/e2eutil/prometheus.go @@ -572,13 +572,14 @@ func createBlock( for _, lset := range batch { var err error - if sampleType == chunkenc.ValFloat { + switch sampleType { + case chunkenc.ValFloat: randMutex.Lock() _, err = app.Append(0, lset, t, r.Float64()) randMutex.Unlock() - } else if sampleType == chunkenc.ValHistogram { + case chunkenc.ValHistogram: _, err = app.AppendHistogram(0, lset, t, &histogramSample, nil) - } else if sampleType == chunkenc.ValFloatHistogram { + case chunkenc.ValFloatHistogram: _, err = app.AppendHistogram(0, lset, t, nil, &floatHistogramSample) } if err != nil { diff --git a/pkg/tracing/tracing_middleware/interceptors_test.go b/pkg/tracing/tracing_middleware/interceptors_test.go index 39f6b4d4516..36976df5a80 100644 --- a/pkg/tracing/tracing_middleware/interceptors_test.go +++ b/pkg/tracing/tracing_middleware/interceptors_test.go @@ -138,9 +138,10 @@ func (s *OpentracingSuite) assertTracesCreated(methodName string) (clientSpan *m assert.Contains(s.T(), span.String(), traceIdAssert, "not part of the fake parent trace: %v", span) if span.OperationName == methodName { kind := fmt.Sprintf("%v", span.Tag("span.kind")) - if kind == "client" { + switch kind { + case "client": clientSpan = span - } else if kind == "server" { + case "server": serverSpan = span } assert.EqualValues(s.T(), span.Tag("component"), "gRPC", "span must be tagged with gRPC component") @@ -232,8 +233,8 @@ func (jaegerFormatExtractor) Extract(carrier interface{}) (mocktracer.MockSpanCo } err := reader.ForeachKey(func(key, val string) error { lowerKey := strings.ToLower(key) - switch { - case lowerKey == traceHeaderName: + switch lowerKey { + case traceHeaderName: parts := strings.Split(val, ":") if len(parts) != 4 { return errors.New("invalid trace id format") diff --git a/test/e2e/e2ethanos/services.go b/test/e2e/e2ethanos/services.go index c8a9e7fc62d..05f308aba0c 100644 --- a/test/e2e/e2ethanos/services.go +++ b/test/e2e/e2ethanos/services.go @@ -1017,7 +1017,7 @@ func NewQueryFrontend(e e2e.Environment, name, downstreamURL string, config quer "--query-range.response-cache-config": string(cacheConfigBytes), } - if !config.QueryRangeConfig.AlignRangeWithStep { + if !config.AlignRangeWithStep { flags["--no-query-range.align-range-with-step"] = "" } @@ -1025,10 +1025,10 @@ func NewQueryFrontend(e e2e.Environment, name, downstreamURL string, config quer flags["--query-frontend.vertical-shards"] = strconv.Itoa(config.NumShards) } - if config.QueryRangeConfig.MinQuerySplitInterval != 0 { - flags["--query-range.min-split-interval"] = config.QueryRangeConfig.MinQuerySplitInterval.String() - flags["--query-range.max-split-interval"] = config.QueryRangeConfig.MaxQuerySplitInterval.String() - flags["--query-range.horizontal-shards"] = strconv.FormatInt(config.QueryRangeConfig.HorizontalShards, 10) + if config.MinQuerySplitInterval != 0 { + flags["--query-range.min-split-interval"] = config.MinQuerySplitInterval.String() + flags["--query-range.max-split-interval"] = config.MaxQuerySplitInterval.String() + flags["--query-range.horizontal-shards"] = strconv.FormatInt(config.HorizontalShards, 10) flags["--query-range.split-interval"] = "0" } diff --git a/test/e2e/query_frontend_test.go b/test/e2e/query_frontend_test.go index 87e6070eb74..b3905fb9ebf 100644 --- a/test/e2e/query_frontend_test.go +++ b/test/e2e/query_frontend_test.go @@ -946,7 +946,6 @@ func TestQueryFrontendTenantForward(t *testing.T) { }, } for _, tc := range tests { - tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() if tc.tenantName == "" { diff --git a/test/e2e/query_test.go b/test/e2e/query_test.go index 16b50546986..320d9207222 100644 --- a/test/e2e/query_test.go +++ b/test/e2e/query_test.go @@ -1522,7 +1522,7 @@ func remoteWrite(ctx context.Context, timeseries []prompb.TimeSeries, addr strin req.Header.Set("X-Prometheus-Remote-Write-Version", "0.1.0") // Execute HTTP request - res, err := promclient.NewDefaultClient().HTTPClient.Do(req.WithContext(ctx)) + res, err := promclient.NewDefaultClient().Do(req.WithContext(ctx)) if err != nil { return err } From da7f42061265dbe5de65a8ba35f4202bb93bbfcb Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 19:41:09 +0000 Subject: [PATCH 3/9] Replace faillint with depguard and forbidigo in golangci-lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Migrate all faillint banned-import rules to depguard (pkg-level bans) - Migrate all faillint banned-function rules to forbidigo with analyze-types and pkg field for alias-safe matching - Remove faillint from Makefile go-lint target - Fix examples path exclusion (examples → ^examples/) to avoid matching filenames Co-Authored-By: Claude Opus 4.6 --- .bingo/golangci-lint.mod | 4 +- .bingo/golangci-lint.sum | 295 +++++++++++++++++++++++++++++++++++++++ .golangci.yml | 46 +++++- Makefile | 15 +- 4 files changed, 341 insertions(+), 19 deletions(-) diff --git a/.bingo/golangci-lint.mod b/.bingo/golangci-lint.mod index 050d7aa0b08..9ec8fc6b1f5 100644 --- a/.bingo/golangci-lint.mod +++ b/.bingo/golangci-lint.mod @@ -1,7 +1,5 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT -go 1.22.1 - -toolchain go1.23.1 +go 1.25.0 require github.com/golangci/golangci-lint/v2 v2.11.1 // cmd/golangci-lint diff --git a/.bingo/golangci-lint.sum b/.bingo/golangci-lint.sum index 337a0345cf6..d448a68809e 100644 --- a/.bingo/golangci-lint.sum +++ b/.bingo/golangci-lint.sum @@ -1,11 +1,15 @@ 4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA= 4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs= +4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A= +4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= 4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a h1:wFEQiK85fRsEVF0CRrPAos5LoAryUsIX1kPW/WrIqFw= 4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= 4d63.com/gochecknoglobals v0.1.0 h1:zeZSRqj5yCg28tCkIV/z/lWbwvNm5qnKVS15PI8nhD0= 4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= 4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc= 4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU= +4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= +4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -194,7 +198,15 @@ cloud.google.com/go/vpcaccess v1.7.4/go.mod h1:lA0KTvhtEOb/VOdnH/gwPuOzGgM+CWsmG cloud.google.com/go/webrisk v1.9.4/go.mod h1:w7m4Ib4C+OseSr2GL66m0zMBywdrVNTDKsdEsfMl7X0= cloud.google.com/go/websecurityscanner v1.6.4/go.mod h1:mUiyMQ+dGpPPRkHgknIZeCzSHJ45+fY4F52nZFDHm2o= cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= +codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY= +codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= +codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI= +codeberg.org/polyfloyd/go-errorlint v1.9.0/go.mod h1:GPRRu2LzVijNn4YkrZYJfatQIdS+TrcK8rL5Xs24qw8= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= +dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y= +dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= +dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo= +dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= @@ -202,6 +214,8 @@ github.com/4meepo/tagalign v1.3.2 h1:1idD3yxlRGV18VjqtDbqYvQ5pXqQS0wO2dn6M3XstvI github.com/4meepo/tagalign v1.3.2/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= +github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8= +github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= github.com/Abirdcfly/dupword v0.0.9 h1:MxprGjKq3yDBICXDgEEsyGirIXfMYXkLNT/agPsE1tk= github.com/Abirdcfly/dupword v0.0.9/go.mod h1:PzmHVLLZ27MvHSzV7eFmMXSFArWXZPZmfuuziuUrf2g= github.com/Abirdcfly/dupword v0.0.12 h1:56NnOyrXzChj07BDFjeRA+IUzSz01jmzEq+G4kEgFhc= @@ -210,6 +224,12 @@ github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11 github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI= github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY= github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM= +github.com/Abirdcfly/dupword v0.1.7 h1:2j8sInznrje4I0CMisSL6ipEBkeJUJAmK1/lfoNGWrQ= +github.com/Abirdcfly/dupword v0.1.7/go.mod h1:K0DkBeOebJ4VyOICFdppB23Q0YMOgVafM0zYW0n9lF4= +github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= +github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= +github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY= +github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= github.com/Antonboom/errname v0.1.6 h1:LzIJZlyLOCSu51o3/t2n9Ck7PcoP9wdbrdaW6J8fX24= github.com/Antonboom/errname v0.1.6/go.mod h1:7lz79JAnuoMNDAWE9MeeIr1/c/VpSUWatBv2FH9NYpI= github.com/Antonboom/errname v0.1.7 h1:mBBDKvEYwPl4WFFNwec1CZO096G6vzK9vvDQzAwkako= @@ -218,16 +238,22 @@ github.com/Antonboom/errname v0.1.10 h1:RZ7cYo/GuZqjr1nuJLNe8ZH+a+Jd9DaZzttWzak9 github.com/Antonboom/errname v0.1.10/go.mod h1:xLeiCIrvVNpUtsN0wxAh05bNIZpqE22/qDMnTBTttiA= github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM= github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns= +github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q= +github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ= github.com/Antonboom/nilnil v0.1.1 h1:PHhrh5ANKFWRBh7TdYmyyq2gyT2lotnvFvvFbylF81Q= github.com/Antonboom/nilnil v0.1.1/go.mod h1:L1jBqoWM7AOeTD+tSquifKSesRHs4ZdaxvZR+xdJEaI= github.com/Antonboom/nilnil v0.1.5 h1:X2JAdEVcbPaOom2TUa1FxZ3uyuUlex0XMLGYMemu6l0= github.com/Antonboom/nilnil v0.1.5/go.mod h1:I24toVuBKhfP5teihGWctrRiPbRKHwZIFOvc6v3HZXk= github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ= github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ= +github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ= +github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II= github.com/Antonboom/testifylint v1.3.1 h1:Uam4q1Q+2b6H7gvk9RQFw6jyVDdpzIirFOOrbs14eG4= github.com/Antonboom/testifylint v1.3.1/go.mod h1:NV0hTlteCkViPW9mSR4wEMfwp+Hs1T3dY60bkvSfhpM= github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck= github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA= +github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ= +github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -243,6 +269,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x52bXVbk= github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0= @@ -251,6 +279,8 @@ github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAO github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= +github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g= +github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k= github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0 h1:LAPPhJ4KR5Z8aKVZF5S48csJkxL5RMKmE/98fMs1u5M= github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0/go.mod h1:LGOGuvEgCfCQsy3JF2tRmpGDpzA53iZfyGEWSPwQ6/4= github.com/GaijinEntertainment/go-exhaustruct/v2 v2.2.0 h1:V9xVvhKbLt7unNEGAruK1xXglyc668Pq3Xx0MNTNqpo= @@ -272,8 +302,12 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/MirrexOne/unqueryvet v1.5.4 h1:38QOxShO7JmMWT+eCdDMbcUgGCOeJphVkzzRgyLJgsQ= +github.com/MirrexOne/unqueryvet v1.5.4/go.mod h1:fs9Zq6eh1LRIhsDIsxf9PONVUjYdFHdtkHIgZdJnyPU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= @@ -285,14 +319,20 @@ github.com/OpenPeeDeeP/depguard/v2 v2.1.0 h1:aQl70G173h/GZYhWf36aE5H0KaujXfVMnn/ github.com/OpenPeeDeeP/depguard/v2 v2.1.0/go.mod h1:PUBgk35fX4i7JDmwzlJwJ+GMe6NfO1723wmJMgPThNQ= github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= +github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= +github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= +github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= +github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -303,12 +343,20 @@ github.com/alexkohler/nakedret/v2 v2.0.2 h1:qnXuZNvv3/AxkAb22q/sEsEpcA99YxLFACDt github.com/alexkohler/nakedret/v2 v2.0.2/go.mod h1:2b8Gkk0GsOrqQv/gPWjNLDSKwG8I5moSXG1K4VIBcTQ= github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg= github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= +github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ= +github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= +github.com/alexkohler/prealloc v1.1.0 h1:cKGRBqlXw5iyQGLYhrXrDlcHxugXpTq4tQ5c91wkf8M= +github.com/alexkohler/prealloc v1.1.0/go.mod h1:fT39Jge3bQrfA7nPMDngUfvUbQGQeJyGQnR+913SCig= +github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc= +github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus= github.com/alingse/asasalint v0.0.10 h1:qqGPDTV0ff0tWHN/nnIlSdjlU/EwRPaUY4SfpE1rnms= github.com/alingse/asasalint v0.0.10/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= +github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= +github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -332,13 +380,19 @@ github.com/ashanbrown/forbidigo v1.3.0 h1:VkYIwb/xxdireGAdJNZoo24O4lmnEWkactplBl github.com/ashanbrown/forbidigo v1.3.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= +github.com/ashanbrown/forbidigo/v2 v2.3.0 h1:OZZDOchCgsX5gvToVtEBoV2UWbFfI6RKQTir2UZzSxo= +github.com/ashanbrown/forbidigo/v2 v2.3.0/go.mod h1:5p6VmsG5/1xx3E785W9fouMxIOkvY2rRV9nMdWadd6c= github.com/ashanbrown/makezero v0.0.0-20210308000810-4155955488a0 h1:27owMIbvO33XL56BKWPy+SCU69I9wPwPXuMf5mAbVGU= github.com/ashanbrown/makezero v0.0.0-20210308000810-4155955488a0/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= +github.com/ashanbrown/makezero/v2 v2.1.0 h1:snuKYMbqosNokUKm+R6/+vOPs8yVAi46La7Ck6QYSaE= +github.com/ashanbrown/makezero/v2 v2.1.0/go.mod h1:aEGT/9q3S8DHeE57C88z2a6xydvgx8J5hgXIGWgo0MY= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -350,6 +404,8 @@ github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7 github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY= github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= +github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= +github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bombsimon/wsl/v3 v3.2.0 h1:x3QUbwW7tPGcCNridvqmhSRthZMTALnkg5/1J+vaUas= @@ -362,6 +418,10 @@ github.com/bombsimon/wsl/v4 v4.2.1 h1:Cxg6u+XDWff75SIFFmNsqnIOgob+Q9hG6y/ioKbRFi github.com/bombsimon/wsl/v4 v4.2.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw= github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= +github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= +github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= +github.com/bombsimon/wsl/v5 v5.6.0 h1:4z+/sBqC5vUmSp1O0mS+czxwH9+LKXtCWtHH9rZGQL8= +github.com/bombsimon/wsl/v5 v5.6.0/go.mod h1:Uqt2EfrMj2NV8UGoN1f1Y3m0NpUVCsUdrNCdet+8LvU= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/breml/bidichk v0.2.3 h1:qe6ggxpTfA8E75hdjWPZ581sY3a2lnl0IRxLQFelECI= @@ -370,26 +430,38 @@ github.com/breml/bidichk v0.2.4 h1:i3yedFWWQ7YzjdZJHnPo9d/xURinSq3OM+gyM43K4/8= github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s= github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= +github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= +github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= github.com/breml/errchkjson v0.3.0 h1:YdDqhfqMT+I1vIxPSas44P+9Z9HzJwCeAzjB8PxP1xw= github.com/breml/errchkjson v0.3.0/go.mod h1:9Cogkyv9gcT8HREpzi3TiqBxCqDzo8awa92zSDFcofU= github.com/breml/errchkjson v0.3.1 h1:hlIeXuspTyt8Y/UmP5qy1JocGNR00KQHgfaNtRAjoxQ= github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U= github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= +github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= +github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/butuzov/ireturn v0.2.0 h1:kCHi+YzC150GE98WFuZQu9yrTn6GEydO2AuPLbTgnO4= github.com/butuzov/ireturn v0.2.0/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0= github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA= +github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= +github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70= github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs= github.com/butuzov/mirror v1.2.0/go.mod h1:DqZZDtzm42wIAIyHXeN8W/qb1EPlb9Qn/if9icBOpdQ= +github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= +github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyyTJ/rMmc= github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= +github.com/catenacyber/perfsprint v0.10.1 h1:u7Riei30bk46XsG8nknMhKLXG9BcXz3+3tl/WpKm0PQ= +github.com/catenacyber/perfsprint v0.10.1/go.mod h1:DJTGsi/Zufpuus6XPGJyKOTMELe347o6akPvWG9Zcsc= github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= +github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc= +github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= @@ -401,12 +473,26 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.6 h1:Tsy7EppNow2pDC0jN7Hsmcb6mHd71ZbI1vFissRBtc0= github.com/charithe/durationcheck v0.0.6/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= github.com/charithe/durationcheck v0.0.9 h1:mPP4ucLrf/rKZiIG/a9IPXHGlh8p4CzgpyTy6EEutYk= github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= +github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk= +github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= +github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/chavacava/garif v0.0.0-20220316182200-5cad0b5181d4 h1:tFXjAxje9thrTF4h57Ckik+scJjTWdwAtZqZPtOT48M= github.com/chavacava/garif v0.0.0-20220316182200-5cad0b5181d4/go.mod h1:W8EnPSQ8Nv4fUjc/v1/8tHFqhuOJXnRub0dTfuAQktU= github.com/chavacava/garif v0.0.0-20220630083739-93517212f375 h1:E7LT642ysztPWE0dfz43cWOvMiF42DyTRC+eZIaO4yI= @@ -432,6 +518,8 @@ github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9 github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= github.com/ckaznocha/intrange v0.2.0 h1:FykcZuJ8BD7oX93YbO1UY9oZtkRbp+1/kJcDjkefYLs= github.com/ckaznocha/intrange v0.2.0/go.mod h1:r5I7nUlAAG56xmkOpw4XVr16BXhwYTUdcuRFeevn1oE= +github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= +github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -474,6 +562,8 @@ github.com/curioswitch/go-reassign v0.1.2 h1:ekM07+z+VFT560Exz4mTv0/s1yU9gem6CJc github.com/curioswitch/go-reassign v0.1.2/go.mod h1:bFJIHgtTM3hRm2sKXSPkbwNjSFyGURQXyn4IXD2qwfQ= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= +github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= +github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= github.com/daixiang0/gci v0.2.8 h1:1mrIGMBQsBu0P7j7m1M8Lb+ZeZxsZL+jyGX4YoMJJpg= github.com/daixiang0/gci v0.2.8/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= github.com/daixiang0/gci v0.3.3 h1:55xJKH7Gl9Vk6oQ1cMkwrDWjAkT1D+D1G9kNmRcAIY4= @@ -490,6 +580,10 @@ github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw= github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ= +github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ= +github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= +github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -502,6 +596,8 @@ github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7q github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -541,6 +637,8 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/firefart/nonamedreturns v1.0.1 h1:fSvcq6ZpK/uBAgJEGMvzErlzyM4NELLqqdTofVjVNag= @@ -549,6 +647,8 @@ github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phm github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= +github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= +github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= @@ -574,6 +674,8 @@ github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlya github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk= github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw= +github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1f+MzD0= +github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI= github.com/go-critic/go-critic v0.5.5 h1:hqPHqQt/2l4Syc2VOIgcuy0FSytcbatOHWggu45vhw8= github.com/go-critic/go-critic v0.5.5/go.mod h1:eMs1Oc/oIP+CYNVN09M+XZYffIPuRHawxzlggAPN9Kk= github.com/go-critic/go-critic v0.6.2 h1:L5SDut1N4ZfsWZY0sH4DCrsHLHnhuuWak2wa165t9gs= @@ -588,6 +690,8 @@ github.com/go-critic/go-critic v0.9.0 h1:Pmys9qvU3pSML/3GEQ2Xd9RZ/ip+aXHKILuxczK github.com/go-critic/go-critic v0.9.0/go.mod h1:5P8tdXL7m/6qnyG6oRAlYLORvoXH0WDypYgAEmagT40= github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU= github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc= +github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog= +github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -671,10 +775,14 @@ github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAp github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY= +github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= @@ -682,12 +790,16 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godoc-lint/godoc-lint v0.11.2 h1:Bp0FkJWoSdNsBikdNgIcgtaoo+xz6I/Y9s5WSBQUeeM= +github.com/godoc-lint/godoc-lint v0.11.2/go.mod h1:iVpGdL1JCikNH2gGeAn3Hh+AgN5Gx/I/cxV+91L41jo= github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= +github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -736,14 +848,20 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0= +github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= +github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo= github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= +github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U= +github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY= @@ -752,6 +870,8 @@ github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZ github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM= github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME= github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE= +github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE= +github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY= github.com/golangci/golangci-lint v1.39.1-0.20210330125642-6844f6abf817 h1:/cj909lXJ48yCH1dmtF7mrm8qH/+aUR15PsC5Oz9SA4= github.com/golangci/golangci-lint v1.39.1-0.20210330125642-6844f6abf817/go.mod h1:tUZzmjq/sO4QmwvxzN5LhVvStPu3OgHtijV8Oqoc/fQ= github.com/golangci/golangci-lint v1.45.2 h1:9I3PzkvscJkFAQpTQi5Ga0V4qWdJERajX1UZ7QqkW+I= @@ -770,6 +890,10 @@ github.com/golangci/golangci-lint v1.59.1 h1:CRRLu1JbhK5avLABFJ/OHVSQ0Ie5c4ulsOI github.com/golangci/golangci-lint v1.59.1/go.mod h1:jX5Oif4C7P0j9++YB2MMJmoNrb01NJ8ITqKWNLewThg= github.com/golangci/golangci-lint v1.61.0 h1:VvbOLaRVWmyxCnUIMTbf1kDsaJbTzH20FAMXTAlQGu8= github.com/golangci/golangci-lint v1.61.0/go.mod h1:e4lztIrJJgLPhWvFPDkhiMwEFRrWlmFbrZea3FsJyN8= +github.com/golangci/golangci-lint/v2 v2.11.1 h1:aGbjflzzKNIdOoq/NawrhFjYpkNY4WzPSeIp2zBbzG8= +github.com/golangci/golangci-lint/v2 v2.11.1/go.mod h1:wexdFBIQNhHNhDe1oqzlGFE5dYUqlfccWJKWjoWF1GI= +github.com/golangci/golines v0.15.0 h1:Qnph25g8Y1c5fdo1X7GaRDGgnMHgnxh4Gk4VfPTtRx0= +github.com/golangci/golines v0.15.0/go.mod h1:AZjXd23tbHMpowhtnGlj9KCNsysj72aeZVVHnVcZx10= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= @@ -782,10 +906,14 @@ github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI= github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= +github.com/golangci/misspell v0.8.0 h1:qvxQhiE2/5z+BVRo1kwYA8yGz+lOlu5Jfvtx2b04Jbg= +github.com/golangci/misspell v0.8.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg= github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= github.com/golangci/modinfo v0.3.4/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM= github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= github.com/golangci/revgrep v0.0.0-20210208091834-cd28932614b5 h1:c9Mqqrm/Clj5biNaG7rABrmwUq88nHh0uABo2b/WYmc= github.com/golangci/revgrep v0.0.0-20210208091834-cd28932614b5/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2 h1:SgM7GDZTxtTTQPU84heOxy34iG5Du7F2jcoZnvp+fXI= @@ -794,10 +922,16 @@ github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs= github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAzs= github.com/golangci/revgrep v0.5.3/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= +github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= +github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= @@ -824,6 +958,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -888,6 +1024,8 @@ github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 h1:mrEEilT github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs= +github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -905,12 +1043,18 @@ github.com/gostaticanalysis/comment v1.4.1 h1:xHopR5L2lRz6OsjH4R2HG5wRhW9ySl3FsH github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= +github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8= +github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc= github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5 h1:rx8127mFPqXXsfPSo8BwnIU97MKFZc89WHAHt8PwDVY= github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70= github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= +github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk= +github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY= github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= +github.com/gostaticanalysis/nilerr v0.1.2 h1:S6nk8a9N8g062nsx63kUkF6AzbHGw7zzyHMcpu52xQU= +github.com/gostaticanalysis/nilerr v0.1.2/go.mod h1:A19UHhoY3y8ahoL7YKz6sdjDtduwTSI4CsymaC2htPA= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -940,6 +1084,8 @@ github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39E github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= @@ -959,10 +1105,14 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -1001,6 +1151,8 @@ github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+ github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4= +github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jingyugao/rowserrcheck v0.0.0-20210315055705-d907ca737bb1 h1:4Rlb26NqzNtbDH69CRpr0vZooj3jAlXTycWCX3xRYAY= github.com/jingyugao/rowserrcheck v0.0.0-20210315055705-d907ca737bb1/go.mod h1:TOQpc2SLx6huPfoFGK3UOnEG+u02D3C1GeosjupAKCA= @@ -1012,6 +1164,8 @@ github.com/jjti/go-spancheck v0.6.1 h1:ZK/wE5Kyi1VX3PJpUO2oEgeoI4FWOUm7Shb2Gbv5o github.com/jjti/go-spancheck v0.6.1/go.mod h1:vF1QkOO159prdo6mHRxak2CpzDpHAfKiPUDP/NeRnX8= github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= +github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8= +github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= @@ -1038,6 +1192,8 @@ github.com/julz/importas v0.0.0-20210228071311-d0bf5cb4e1db h1:ZmwBthGFMVAieuVpL github.com/julz/importas v0.0.0-20210228071311-d0bf5cb4e1db/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= +github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= +github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/junk1tm/musttag v0.4.4 h1:VK4L7v7lvWAhKDDx0cUJgbb0UBNipYinv8pPeHJzH9Q= @@ -1045,6 +1201,8 @@ github.com/junk1tm/musttag v0.4.4/go.mod h1:XkcL/9O6RmD88JBXb+I15nYRl9W4ExhgQeCB github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos= github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k= +github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0= +github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -1059,6 +1217,8 @@ github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= +github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw= +github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkHAIKE/contextcheck v1.1.3 h1:l4pNvrb8JSwRd51ojtcOxOeHJzHek+MtOyXbaR0uvmw= @@ -1067,6 +1227,8 @@ github.com/kkHAIKE/contextcheck v1.1.4 h1:B6zAaLhOEEcjvUgIYEqystmnFk1Oemn8bvJhbt github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg= github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg= github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA= +github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= +github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -1095,6 +1257,8 @@ github.com/kulti/thelper v0.6.2 h1:K4xulKkwOCnT1CDms6Ex3uG1dvSMUUQe9zxgYQgbRXs= github.com/kulti/thelper v0.6.2/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= +github.com/kulti/thelper v0.7.1 h1:fI8QITAoFVLx+y+vSyuLBP+rcVIB8jKooNSCT2EiI98= +github.com/kulti/thelper v0.7.1/go.mod h1:NsMjfQEy6sd+9Kfw8kCP61W1I0nerGSYSFnGaxQkcbs= github.com/kunwardeep/paralleltest v1.0.2 h1:/jJRv0TiqPoEy/Y8dQxCFJhD56uS/pnvtatgTZBHokU= github.com/kunwardeep/paralleltest v1.0.2/go.mod h1:ZPqNm1fVHPllh5LPVujzbVz1JN2GhLxSfY+oqUsvG30= github.com/kunwardeep/paralleltest v1.0.3 h1:UdKIkImEAXjR1chUWLn+PNXqWUGs//7tzMeWuP7NhmI= @@ -1105,6 +1269,8 @@ github.com/kunwardeep/paralleltest v1.0.8 h1:Ul2KsqtzFxTlSU7IP0JusWlLiNqQaloB9vg github.com/kunwardeep/paralleltest v1.0.8/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/kunwardeep/paralleltest v1.0.15 h1:ZMk4Qt306tHIgKISHWFJAO1IDQJLc6uDyJMLyncOb6w= +github.com/kunwardeep/paralleltest v1.0.15/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M= github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= @@ -1112,6 +1278,10 @@ github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I= github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0= +github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= +github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= +github.com/ldez/exptostd v0.4.5 h1:kv2ZGUVI6VwRfp/+bcQ6Nbx0ghFWcGIKInkG/oFn1aQ= +github.com/ldez/exptostd v0.4.5/go.mod h1:QRjHRMXJrCTIm9WxVNH6VW7oN7KrGSht69bIRwvdFsM= github.com/ldez/gomoddirectives v0.2.1 h1:9pAcW9KRZW7HQjFwbozNvFMcNVwdCBufU7os5QUwLIY= github.com/ldez/gomoddirectives v0.2.1/go.mod h1:sGicqkRgBOg//JfpXwkB9Hj0X5RyJ7mlACM5B9f6Me4= github.com/ldez/gomoddirectives v0.2.2 h1:p9/sXuNFArS2RLc+UpYZSI4KQwGMEDWC/LbtF5OPFVg= @@ -1120,12 +1290,22 @@ github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUc github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg= github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g= +github.com/ldez/gomoddirectives v0.8.0 h1:JqIuTtgvFC2RdH1s357vrE23WJF2cpDCPFgA/TWDGpk= +github.com/ldez/gomoddirectives v0.8.0/go.mod h1:jutzamvZR4XYJLr0d5Honycp4Gy6GEg2mS9+2YX3F1Q= +github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o= +github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas= +github.com/ldez/structtags v0.6.1 h1:bUooFLbXx41tW8SvkfwfFkkjPYvFFs59AAMgVg6DUBk= +github.com/ldez/structtags v0.6.1/go.mod h1:YDxVSgDy/MON6ariaxLF2X09bh19qL7MtGBN5MrvbdY= github.com/ldez/tagliatelle v0.3.1 h1:3BqVVlReVUZwafJUwQ+oxbx2BEX2vUG4Yu/NOfMiKiM= github.com/ldez/tagliatelle v0.3.1/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= github.com/ldez/tagliatelle v0.4.0 h1:sylp7d9kh6AdXN2DpVGHBRb5guTVAgOxqNGhbqc4b1c= github.com/ldez/tagliatelle v0.4.0/go.mod h1:mNtTfrHy2haaBAw+VT7IBV6VXBThS7TCreYWbBcJ87I= github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= +github.com/ldez/tagliatelle v0.7.2 h1:KuOlL70/fu9paxuxbeqlicJnCspCRjH0x8FW+NfgYUk= +github.com/ldez/tagliatelle v0.7.2/go.mod h1:PtGgm163ZplJfZMZ2sf5nhUT170rSuPgBimoyYtdaSI= +github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc= +github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leonklingele/grouper v1.1.0 h1:tC2y/ygPbMFSBOs3DcyaEMKnnwH7eYKzohOtRrf0SAg= github.com/leonklingele/grouper v1.1.0/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= @@ -1143,6 +1323,8 @@ github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= @@ -1151,6 +1333,8 @@ github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuz github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= +github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= +github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U= github.com/magefile/mage v1.13.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -1161,18 +1345,28 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww= +github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM= +github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8= +github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= +github.com/maratori/testableexamples v1.0.1 h1:HfOQXs+XgfeRBJ+Wz0XfH+FHnoY9TVqL6Fcevpzy4q8= +github.com/maratori/testableexamples v1.0.1/go.mod h1:XE2F/nQs7B9N08JgyRmdGjYVGqxWwClLPCGSQhXQSrQ= github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/maratori/testpackage v1.1.0 h1:GJY4wlzQhuBusMF1oahQCBtUV/AQ/k69IZ68vxaac2Q= github.com/maratori/testpackage v1.1.0/go.mod h1:PeAhzU8qkCwdGEMTEupsHJNlQu2gZopMC6RjbhmHeDc= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc= +github.com/maratori/testpackage v1.1.2 h1:ffDSh+AgqluCLMXhM19f/cpvQAKygKAJXFl9aUjmbqs= +github.com/maratori/testpackage v1.1.2/go.mod h1:8F24GdVDFW5Ew43Et02jamrVMNXLUNaOynhDssITGfc= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2srm/LN17lpybq15AryXIRcWYLE= github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= +github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -1184,6 +1378,8 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -1204,6 +1400,8 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= @@ -1231,6 +1429,8 @@ github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE= github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA= github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A= github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU= +github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q= +github.com/mgechev/revive v1.15.0/go.mod h1:LlAKO3QQe9OJ0pVZzI2GPa8CbXGZ/9lNpCGvK4T/a8A= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= @@ -1274,6 +1474,8 @@ github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGq github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= @@ -1308,6 +1510,8 @@ github.com/nunnatsa/ginkgolinter v0.13.3 h1:wEvjrzSMfDdnoWkctignX9QTf4rT9f4GkQ3u github.com/nunnatsa/ginkgolinter v0.13.3/go.mod h1:aTKXo8WddENYxNEFT+4ZxEgWXqlD9uMD3w9Bfw/ABEc= github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk= github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ= +github.com/nunnatsa/ginkgolinter v0.23.0 h1:x3o4DGYOWbBMP/VdNQKgSj+25aJKx2Pe6lHr8gBcgf8= +github.com/nunnatsa/ginkgolinter v0.23.0/go.mod h1:9qN1+0akwXEccwV1CAcCDfcoBlWXHB+ML9884pL4SZ4= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -1384,6 +1588,8 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= @@ -1479,6 +1685,8 @@ github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard v0.4.5 h1:AGY0tiOT5hJX9BTdx/xBdoCubQUAE2grkqY2lSwvZcA= +github.com/quasilyte/go-ruleguard v0.4.5/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.1/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101150716-969a394a9451/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= @@ -1489,6 +1697,8 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.19/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQ github.com/quasilyte/go-ruleguard/dsl v0.3.21/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY= +github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/go-ruleguard/rules v0.0.0-20210221215616-dfcc94e3dffd/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= @@ -1507,11 +1717,15 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= +github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= +github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/go-dbus v0.0.0-20121104212943-b7232d34b1d5/go.mod h1:+u151txRmLpwxBmpYn9z3d1sdJdjRPQpsXuYeY9jNls= github.com/remyoudompheng/go-liblzma v0.0.0-20190506200333-81bf2d431b96/go.mod h1:90HvCY7+oHHUKkbeMCiHt1WuFR2/hPJ9QrljDG+v6ls= github.com/remyoudompheng/go-misc v0.0.0-20190427085024-2d6ac652a50e/go.mod h1:80FQABjoFzZ2M5uEa6FUaJYEmqU2UOKojlFVak1UAwI= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1521,6 +1735,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1539,6 +1755,8 @@ github.com/ryancurrah/gomodguard v1.3.2 h1:CuG27ulzEB1Gu5Dk5gP8PFxSOZ3ptSdP5iI/3 github.com/ryancurrah/gomodguard v1.3.2/go.mod h1:LqdemiFomEjcxOqirbQCb3JFvSxH2JUYMerTFd3sF2o= github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= +github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g= +github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw= github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanrolds/sqlclosecheck v0.4.0 h1:i8SX60Rppc1wRuyQjMciLqIzV3xnoHB7/tXbr6RGYNI= @@ -1557,8 +1775,12 @@ github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3 github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= +github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= +github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= github.com/sashamelentyev/usestdlibvars v1.13.0 h1:uObNudVEEHf6JbOJy5bgKJloA1bWjxR9fwgNFpPzKnI= @@ -1571,6 +1793,8 @@ github.com/sashamelentyev/usestdlibvars v1.26.0 h1:LONR2hNVKxRmzIrZR0PhSF3mhCAzv github.com/sashamelentyev/usestdlibvars v1.26.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI= github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= +github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ= +github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/securego/gosec/v2 v2.7.0 h1:mOhJv5w6UyNLpSssQOQCc7eGkKLuicAxvf66Ey/X4xk= github.com/securego/gosec/v2 v2.7.0/go.mod h1:xNbGArrGUspJLuz3LS5XCY1EBW/0vABAl/LWfSklmiM= @@ -1590,6 +1814,8 @@ github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 h1:rnO6Zp1YMQ github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9/go.mod h1:dg7lPlu/xK/Ut9SedURCoZbVCR4yC7fM65DtH9/CDHs= github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyEl3M= github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU= +github.com/securego/gosec/v2 v2.24.7 h1:3k5yJnrhT1TTdsG0ZsnenlfCcT+7Y/+zeCPHbL7QAn8= +github.com/securego/gosec/v2 v2.24.7/go.mod h1:AdDJbjcG/XxFgVv7pW19vMNYlFM6+Q6Qy3t6lWAUcEY= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= @@ -1616,6 +1842,8 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI= github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= @@ -1641,6 +1869,8 @@ github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00= github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo= +github.com/sonatard/noctx v0.5.0 h1:e/jdaqAsuWVOKQ0P6NWiIdDNHmHT5SwuuSfojFjzwrw= +github.com/sonatard/noctx v0.5.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas= github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= @@ -1655,6 +1885,8 @@ github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= @@ -1676,6 +1908,8 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -1684,6 +1918,8 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= @@ -1702,6 +1938,8 @@ github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YE github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= +github.com/stbenjam/no-sprintf-host-port v0.3.1 h1:AyX7+dxI4IdLBPtDbsGAyqiTSLpCP9hWRrXQDU4Cm/g= +github.com/stbenjam/no-sprintf-host-port v0.3.1/go.mod h1:ODbZesTCHMVKthBHskvUUexdcNHAQRXk9NpSsL8p/HQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1733,6 +1971,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= @@ -1762,6 +2002,8 @@ github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= github.com/tetafro/godot v1.4.17 h1:pGzu+Ye7ZUEFx7LHU0dAKmCOXWsPjl7qA6iMGndsjPs= github.com/tetafro/godot v1.4.17/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg= +github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 h1:kl4KhGNsJIbDHS9/4U9yQo1UcPQM0kOMJHn29EoH/Ro= @@ -1770,10 +2012,14 @@ github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e h1:MV6KaVu/hzByH github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= +github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk= +github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= github.com/timonwong/loggercheck v0.9.3 h1:ecACo9fNiHxX4/Bc02rW2+kaJIAMAes7qJ7JKxt0EZI= github.com/timonwong/loggercheck v0.9.3/go.mod h1:wUqnk9yAOIKtGA39l1KLE9Iz0QiTocu/YZoOf+OzFdw= github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= +github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= +github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= github.com/timonwong/logrlint v0.1.0 h1:phZCcypL/vtx6cGxObJgWZ5wexZF5SXFPLOM+ru0e/M= github.com/timonwong/logrlint v0.1.0/go.mod h1:Zleg4Gw+kRxNej+Ra7o+tEaW5k1qthTaYKU7rSD39LU= github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= @@ -1805,6 +2051,8 @@ github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3b github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4= github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= +github.com/tomarrell/wrapcheck/v2 v2.12.0 h1:H/qQ1aNWz/eeIhxKAFvkfIA+N7YDvq6TWVFL27Of9is= +github.com/tomarrell/wrapcheck/v2 v2.12.0/go.mod h1:AQhQuZd0p7b6rfW+vUwHm5OMCGgp63moQ9Qr/0BpIWo= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/tommy-muehle/go-mnd/v2 v2.3.1 h1:a1S4+4HSXDJMgeODJH/t0EEKxcVla6Tasw+Zx9JJMog= github.com/tommy-muehle/go-mnd/v2 v2.3.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= @@ -1818,12 +2066,16 @@ github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iL github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= +github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI= +github.com/ultraware/funlen v0.2.0/go.mod h1:ZE0q4TsJ8T1SQcjmkhN/w+MceuatI6pBFSxxyteHIJA= github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg= github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ= github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= +github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSWoFa+g= +github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs= @@ -1838,6 +2090,10 @@ github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvni github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k= github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM= github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= +github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS4= +github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q= +github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU= +github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= @@ -1850,8 +2106,12 @@ github.com/xen0n/gosmopolitan v1.2.1 h1:3pttnTuFumELBRSh+KQs1zcz4fN6Zy7aB0xlnQSn github.com/xen0n/gosmopolitan v1.2.1/go.mod h1:JsHq/Brs1o050OOdmzHeOr0N7OtlnKRAGAsElF8xBQA= github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= +github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= +github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= @@ -1897,10 +2157,18 @@ go-simpler.org/assert v0.5.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs= go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM= +go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo= +go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE= go-simpler.org/sloglint v0.7.1 h1:qlGLiqHbN5islOxjeLXoPtUdZXb669RW+BDQ+xOSNoU= go-simpler.org/sloglint v0.7.1/go.mod h1:OlaVDRh/FKKd4X4sIMbsz8st97vomydceL146Fthh/c= go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= +go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s= +go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ= +go.augendre.info/arangolint v0.4.0 h1:xSCZjRoS93nXazBSg5d0OGCi9APPLNMmmLrC995tR50= +go.augendre.info/arangolint v0.4.0/go.mod h1:l+f/b4plABuFISuKnTGD4RioXiCCgghv2xqst/xOvAA= +go.augendre.info/fatcontext v0.9.0 h1:Gt5jGD4Zcj8CDMVzjOJITlSb9cEch54hjRRlN3qDojE= +go.augendre.info/fatcontext v0.9.0/go.mod h1:L94brOAT1OOUNue6ph/2HnwxoNlds9aXDF2FcUntbNw= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -1944,6 +2212,8 @@ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+ go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= @@ -1951,6 +2221,10 @@ go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -2005,6 +2279,7 @@ golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcH golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= @@ -2018,6 +2293,8 @@ golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 h1:jWGQJV4niP+CCm golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 h1:qWFG1Dj7TBjOjOvhEOkmyGPVoquqUKnIU0lEVLp8xyk= +golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -2074,6 +2351,8 @@ golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2199,6 +2478,8 @@ golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2341,6 +2622,8 @@ golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2386,6 +2669,8 @@ golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2533,6 +2818,8 @@ golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2752,6 +3039,8 @@ google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGm google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2820,6 +3109,8 @@ honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs= honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= +honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= +honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= @@ -2862,6 +3153,8 @@ mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= +mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= +mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= @@ -2876,6 +3169,8 @@ mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d h1:3rvTIIM22r9pvXk+q3swxUQAQ mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= +mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 h1:ssMzja7PDPJV8FStj7hq9IKiuiKhgz9ErWw+m68e7DI= +mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15/go.mod h1:4M5MMXl2kW6fivUT6yRGpLLPNfuGtU2Z0cPvFquGDYU= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/.golangci.yml b/.golangci.yml index 9293597f904..2a602adcc20 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,12 +10,50 @@ output: linters: enable: - copyloopvar + - depguard + - forbidigo - goconst - godot - misspell - promlinter - unparam settings: + depguard: + # TODO(bwplotka): Add Printf, DefaultRegisterer, NewGaugeFunc and MustRegister + # once exceptions are accepted. Add fmt.Errorf → github.com/pkg/errors.Errorf + # once https://github.com/fatih/faillint/issues/10 is addressed. + rules: + main: + deny: + - pkg: github.com/prometheus/tsdb + desc: "use github.com/prometheus/prometheus/tsdb instead" + - pkg: github.com/prometheus/prometheus/pkg/testutils + desc: "use github.com/thanos-io/thanos/pkg/testutil instead" + - pkg: sync/atomic + desc: "use go.uber.org/atomic instead" + - pkg: github.com/cortexproject/cortex + desc: "use github.com/thanos-io/thanos/internal/cortex instead" + - pkg: io/ioutil + desc: "deprecated: use io and os packages instead" + - pkg: github.com/NYTimes/gziphandler + desc: "use github.com/klauspost/compress/gzhttp instead" + forbidigo: + exclude-godoc-examples: true + analyze-types: true + forbid: + - pattern: '^fmt\.Print(|ln)$' + msg: "use a logger instead" + - pattern: '^fmt\.Sprint$' + msg: "use a logger instead" + - pattern: '^prometheus\.(DefaultGatherer|DefBuckets|NewUntypedFunc|UntypedFunc)$' + pkg: '^github\.com/prometheus/client_golang/prometheus$' + msg: "banned prometheus function" + - pattern: '^prometheus\.(NewCounter|NewCounterVec|NewGauge|NewGaugeVec|NewGaugeFunc|NewHistorgram|NewHistogramVec|NewSummary|NewSummaryVec)$' + pkg: '^github\.com/prometheus/client_golang/prometheus$' + msg: "use promauto equivalent instead" + - pattern: '^parser\.(ParseExpr|ParseMetricSelector)$' + pkg: '^github\.com/prometheus/prometheus/promql/parser$' + msg: "use github.com/thanos-io/thanos/pkg/extpromql instead" errcheck: exclude-functions: - (github.com/go-kit/log.Logger).Log @@ -57,12 +95,16 @@ linters: - linters: - unparam path: test/e2e/compatibility_test\.go + # fmt.Print/Println/Sprint are allowed in tests. + - linters: + - forbidigo + path: _test\.go paths: - vendor - internal/cortex - third_party$ - builtin$ - - examples + - ^examples/ formatters: enable: - gofmt @@ -74,4 +116,4 @@ formatters: - internal/cortex - third_party$ - builtin$ - - examples + - ^examples/ diff --git a/Makefile b/Makefile index fd3f4acc619..62d3e0afdde 100644 --- a/Makefile +++ b/Makefile @@ -398,21 +398,8 @@ lint: go-lint react-app-lint shell-lint # --mem-profile-path string Path to memory profile output file # to debug big allocations during linting. .PHONY: go-lint -go-lint: check-git deps $(GOLANGCI_LINT) $(FAILLINT) +go-lint: check-git deps $(GOLANGCI_LINT) $(call require_clean_work_tree,'detected not clean work tree before running lint, previous job changed something?') - @echo ">> verifying modules being imported" - @# TODO(bwplotka): Add, Printf, DefaultRegisterer, NewGaugeFunc and MustRegister once exception are accepted. Add fmt.{Errorf}=github.com/pkg/errors.{Errorf} once https://github.com/fatih/faillint/issues/10 is addressed. - @$(FAILLINT) -paths "github.com/prometheus/tsdb=github.com/prometheus/prometheus/tsdb,\ -github.com/prometheus/prometheus/pkg/testutils=github.com/thanos-io/thanos/pkg/testutil,\ -github.com/prometheus/client_golang/prometheus.{DefaultGatherer,DefBuckets,NewUntypedFunc,UntypedFunc},\ -github.com/prometheus/client_golang/prometheus.{NewCounter,NewCounterVec,NewCounterVec,NewGauge,NewGaugeVec,NewGaugeFunc,\ -NewHistorgram,NewHistogramVec,NewSummary,NewSummaryVec}=github.com/prometheus/client_golang/prometheus/promauto.{NewCounter,\ -NewCounterVec,NewCounterVec,NewGauge,NewGaugeVec,NewGaugeFunc,NewHistorgram,NewHistogramVec,NewSummary,NewSummaryVec},\ -github.com/NYTimes/gziphandler.{GzipHandler}=github.com/klauspost/compress/gzhttp.{GzipHandler},\ -sync/atomic=go.uber.org/atomic,github.com/cortexproject/cortex=github.com/thanos-io/thanos/internal/cortex,\ -github.com/prometheus/prometheus/promql/parser.{ParseExpr,ParseMetricSelector}=github.com/thanos-io/thanos/pkg/extpromql.{ParseExpr,ParseMetricSelector},\ -io/ioutil.{Discard,NopCloser,ReadAll,ReadDir,ReadFile,TempDir,TempFile,Writefile}" $(shell go list ./... | grep -v "internal/cortex") - @$(FAILLINT) -paths "fmt.{Print,Println,Sprint}" -ignore-tests ./... @echo ">> linting all of the Go files GOGC=${GOGC}" @$(GOLANGCI_LINT) run @echo ">> ensuring Copyright headers" From b6f048eba1acc8b3973938a9504ee69925b4f14b Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 19:51:03 +0000 Subject: [PATCH 4/9] fix go mod file for bingo golangci-lint.mod --- .bingo/golangci-lint.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bingo/golangci-lint.mod b/.bingo/golangci-lint.mod index 9ec8fc6b1f5..4a88ded163b 100644 --- a/.bingo/golangci-lint.mod +++ b/.bingo/golangci-lint.mod @@ -1,5 +1,5 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT -go 1.25.0 +go 1.23.0 require github.com/golangci/golangci-lint/v2 v2.11.1 // cmd/golangci-lint From 5585ff9d50a2b69f00c33b218b1ee55e1e4ea865 Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 20:00:13 +0000 Subject: [PATCH 5/9] runners must move up to 1.25 too-- this is good long term --- .circleci/config.yml | 4 ++-- .github/workflows/go.yaml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1aba7ee8e0d..b485dd6a55a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,10 +8,10 @@ orbs: executors: golang: docker: - - image: cimg/go:1.23-node + - image: cimg/go:1.25-node golang-test: docker: - - image: cimg/go:1.23-node + - image: cimg/go:1.25-node - image: quay.io/thanos/docker-swift-onlyone-authv2-keystone:v0.1 jobs: diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml index da7a9db7d77..5de348d7e68 100644 --- a/.github/workflows/go.yaml +++ b/.github/workflows/go.yaml @@ -34,7 +34,7 @@ jobs: - name: Install Go. uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: 1.23.x + go-version: 1.25.x - name: Install bingo modules run: make install-tool-deps @@ -55,7 +55,7 @@ jobs: - name: Install Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: 1.23.x + go-version: 1.25.x - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 with: @@ -82,7 +82,7 @@ jobs: - name: Install Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: 1.23.x + go-version: 1.25.x - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 with: @@ -109,7 +109,7 @@ jobs: - name: Install Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: 1.23.x + go-version: 1.25.x - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 with: @@ -158,7 +158,7 @@ jobs: - name: Install Go. uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: 1.23.x + go-version: 1.25.x - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 with: From 5f08c00a6f7fcaf0fa0f7e68a4fc1901818fdfd4 Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 20:10:44 +0000 Subject: [PATCH 6/9] bingo auto updated something --- .bingo/golangci-lint.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bingo/golangci-lint.mod b/.bingo/golangci-lint.mod index 4a88ded163b..9ec8fc6b1f5 100644 --- a/.bingo/golangci-lint.mod +++ b/.bingo/golangci-lint.mod @@ -1,5 +1,5 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT -go 1.23.0 +go 1.25.0 require github.com/golangci/golangci-lint/v2 v2.11.1 // cmd/golangci-lint From e729eb3e331416ec317e69b63eb2309b3dc9a349 Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 20:41:06 +0000 Subject: [PATCH 7/9] revert accidental change --- Makefile | 6 ++++++ pkg/discovery/memcache/resolver_test.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 62d3e0afdde..b3f598d1a51 100644 --- a/Makefile +++ b/Makefile @@ -408,6 +408,12 @@ go-lint: check-git deps $(GOLANGCI_LINT) @$(MAKE) proto $(call require_clean_work_tree,'detected files without copyright, run make lint and commit changes') +.PHONY: lint-local +lint-local: ## Runs Go linters locally without clean work tree checks. +lint-local: check-git deps $(GOLANGCI_LINT) + @echo ">> linting all of the Go files GOGC=${GOGC}" + @$(GOLANGCI_LINT) run + .PHONY: shell-lint shell-lint: ## Runs static analysis against our shell scripts. shell-lint: $(SHELLCHECK) diff --git a/pkg/discovery/memcache/resolver_test.go b/pkg/discovery/memcache/resolver_test.go index 3dbbe62154c..2688d1b3bd4 100644 --- a/pkg/discovery/memcache/resolver_test.go +++ b/pkg/discovery/memcache/resolver_test.go @@ -62,7 +62,7 @@ func TestBadClusterConfigs(t *testing.T) { errors.New("failed to read nodes: EOF"), }, {"CONFIG cluster 0 0\r\n100\r\ndns-1|ip-1|11211\r\nEND\r\n", - errors.New("expected 0 in config payload, but got 23 instead."), + errors.New("expected 0 in config payload, but got 23 instead"), }, {"CONFIG cluster 0 17\r\n100\r\ndns-1|ip-1\r\nEND\r\n", errors.New("node not in expected format: [dns-1 ip-1]"), From 8d3b35951217c4700d91c51e80712fd5f464ff93 Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 20:56:19 +0000 Subject: [PATCH 8/9] adds missing makefile updates --- Makefile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b3f598d1a51..c4768ebc4a3 100644 --- a/Makefile +++ b/Makefile @@ -285,10 +285,9 @@ format: go-format shell-format .PHONY: go-format go-format: ## Formats Go code including imports. -go-format: $(GOIMPORTS) +go-format: $(GOLANGCI_LINT) @echo ">> formatting go code" - @gofmt -s -w $(FILES_TO_FMT) - @$(GOIMPORTS) -w $(FILES_TO_FMT) + @$(GOLANGCI_LINT) fmt .PHONY: proto proto: ## Generates Go files from Thanos proto files. @@ -414,6 +413,13 @@ lint-local: check-git deps $(GOLANGCI_LINT) @echo ">> linting all of the Go files GOGC=${GOGC}" @$(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: ## Runs Go linters and auto-fixes issues where possible. +lint-fix: check-git deps $(GOLANGCI_LINT) + @echo ">> linting and fixing Go files GOGC=${GOGC}" + @$(GOLANGCI_LINT) run --fix + .PHONY: shell-lint shell-lint: ## Runs static analysis against our shell scripts. shell-lint: $(SHELLCHECK) From 6d4a9988d19777aa049c8c95942caac1b7d03314 Mon Sep 17 00:00:00 2001 From: Gavin Shriver Date: Fri, 6 Mar 2026 21:17:15 +0000 Subject: [PATCH 9/9] Retrigger CI/CD Co-Authored-By: Claude Opus 4.6