Skip to content

Commit 1e89084

Browse files
committed
fix linter issues throughout the repo
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
1 parent a243ea3 commit 1e89084

82 files changed

Lines changed: 1558 additions & 1504 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
2929
- **BREAKING**: Enhanced node discovery to match OpenSearch server behavior ([#765](https://github.com/opensearch-project/opensearch-go/issues/765))
3030
- Dedicated cluster manager nodes are now excluded from client request routing by default (best practice)
3131
- Node selection logic now matches Java client `NodeSelector.SKIP_DEDICATED_CLUSTER_MASTERS` behavior
32+
- **BREAKING**: Add context support to discovery and client lifecycle management
33+
- `opensearchtransport.Discoverable` interface now requires `context.Context` parameter: `DiscoverNodes(ctx context.Context) error`
34+
- `opensearch.Client.DiscoverNodes()` and `opensearchtransport.Client.DiscoverNodes()` now require `context.Context` parameter
35+
- `opensearch.Config` and `opensearchtransport.Config` now accept optional `Context` and `CancelFunc` fields
36+
- `opensearchutil.BulkIndexerConfig` now accepts optional `Context` and `CancelFunc` fields
37+
- Enables proper context propagation for timeouts, cancellation, and graceful shutdown
3238

3339
### Deprecated
3440

internal/test/helper.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func waitForClusterReady(t *testing.T, client *opensearchapi.Client) error {
5757
)
5858

5959
// Get version for informational logging
60-
major, minor, patch, err := GetVersion(client, t)
60+
major, minor, patch, err := GetVersion(t, client)
6161
if err != nil {
6262
return fmt.Errorf("failed to get OpenSearch version: %w", err)
6363
}
@@ -116,7 +116,8 @@ func extendedReadinessCheck(ctx context.Context, client *opensearchapi.Client) e
116116
}
117117

118118
// GetVersion gets cluster info and returns version as int's
119-
func GetVersion(client *opensearchapi.Client, t *testing.T) (int64, int64, int64, error) {
119+
func GetVersion(t *testing.T, client *opensearchapi.Client) (int64, int64, int64, error) {
120+
t.Helper()
120121
if client == nil {
121122
return 0, 0, 0, fmt.Errorf("client cannot be nil")
122123
}
@@ -130,7 +131,7 @@ func GetVersion(client *opensearchapi.Client, t *testing.T) (int64, int64, int64
130131
// SkipIfBelowVersion skips a test if the cluster version is below a given version
131132
func SkipIfBelowVersion(t *testing.T, client *opensearchapi.Client, majorVersion, patchVersion int64, testName string) {
132133
t.Helper()
133-
major, patch, _, err := GetVersion(client, t)
134+
major, patch, _, err := GetVersion(t, client)
134135
assert.Nil(t, err)
135136
if major < majorVersion || (major == majorVersion && patch < patchVersion) {
136137
t.Skipf("Skipping %s as version %d.%d.x does not support this endpoint", testName, major, patch)

internal/test/readiness_test.go

Lines changed: 29 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// compatible open source license.
66
//go:build integration
77

8-
package ostest
8+
package ostest_test
99

1010
import (
1111
"context"
@@ -16,13 +16,14 @@ import (
1616
"github.com/stretchr/testify/require"
1717
"github.com/stretchr/testify/suite"
1818

19+
ostest "github.com/opensearch-project/opensearch-go/v4/internal/test"
1920
"github.com/opensearch-project/opensearch-go/v4/opensearchapi"
2021
"github.com/opensearch-project/opensearch-go/v4/opensearchtransport/testutil"
2122
)
2223

2324
// TestNewClient demonstrates the enhanced client creation with automatic readiness checks
2425
func TestNewClient(t *testing.T) {
25-
client, err := NewClient(t)
26+
client, err := ostest.NewClient(t)
2627
require.NoError(t, err, "Failed to create client")
2728
require.NotNil(t, client, "Client should not be nil")
2829

@@ -42,23 +43,23 @@ func TestNewClient(t *testing.T) {
4243

4344
// TestConfigFunctions tests the configuration utility functions
4445
func TestConfigFunctions(t *testing.T) {
45-
// Test IsSecure function
46-
t.Run("IsSecure", func(t *testing.T) {
46+
// Test ostest.IsSecure function
47+
t.Run("ostest.IsSecure", func(t *testing.T) {
4748
// Save original value
4849
original := os.Getenv("SECURE_INTEGRATION")
4950
defer os.Setenv("SECURE_INTEGRATION", original)
5051

5152
// Test false case
5253
os.Setenv("SECURE_INTEGRATION", "false")
53-
assert.False(t, IsSecure())
54+
assert.False(t, ostest.IsSecure())
5455

5556
// Test true case
5657
os.Setenv("SECURE_INTEGRATION", "true")
57-
assert.True(t, IsSecure())
58+
assert.True(t, ostest.IsSecure())
5859

5960
// Test empty case
6061
os.Unsetenv("SECURE_INTEGRATION")
61-
assert.False(t, IsSecure())
62+
assert.False(t, ostest.IsSecure())
6263
})
6364

6465
// Test GetPassword function
@@ -76,31 +77,31 @@ func TestConfigFunctions(t *testing.T) {
7677

7778
// Test default admin password for older versions
7879
os.Setenv("OPENSEARCH_VERSION", "2.0.0")
79-
password, err := GetPassword()
80+
password, err := ostest.GetPassword()
8081
assert.NoError(t, err)
8182
assert.Equal(t, "admin", password)
8283

8384
// Test strong password for newer versions
8485
os.Setenv("OPENSEARCH_VERSION", "2.12.0")
85-
password, err = GetPassword()
86+
password, err = ostest.GetPassword()
8687
assert.NoError(t, err)
8788
assert.Equal(t, "myStrongPassword123!", password)
8889

8990
// Test latest version
9091
os.Setenv("OPENSEARCH_VERSION", "latest")
91-
password, err = GetPassword()
92+
password, err = ostest.GetPassword()
9293
assert.NoError(t, err)
9394
assert.Equal(t, "myStrongPassword123!", password)
9495

9596
// Test empty version
9697
os.Unsetenv("OPENSEARCH_VERSION")
97-
password, err = GetPassword()
98+
password, err = ostest.GetPassword()
9899
assert.NoError(t, err)
99100
assert.Equal(t, "myStrongPassword123!", password)
100101

101102
// ERROR PATH: Test invalid version format
102103
os.Setenv("OPENSEARCH_VERSION", "invalid.version")
103-
_, err = GetPassword()
104+
_, err = ostest.GetPassword()
104105
assert.Error(t, err, "Should error with invalid version format")
105106
})
106107

@@ -125,7 +126,7 @@ func TestConfigFunctions(t *testing.T) {
125126

126127
// Test insecure config (should return valid HTTP config)
127128
os.Setenv("SECURE_INTEGRATION", "false")
128-
config, err := ClientConfig()
129+
config, err := ostest.ClientConfig()
129130
assert.NoError(t, err)
130131
assert.NotNil(t, config)
131132
assert.Equal(t, []string{"http://localhost:9200"}, config.Client.Addresses)
@@ -136,7 +137,7 @@ func TestConfigFunctions(t *testing.T) {
136137
// Test secure config - success case
137138
os.Setenv("SECURE_INTEGRATION", "true")
138139
os.Setenv("OPENSEARCH_VERSION", "2.12.0") // Valid version
139-
config, err = ClientConfig()
140+
config, err = ostest.ClientConfig()
140141
assert.NoError(t, err)
141142
if config != nil {
142143
assert.Equal(t, "admin", config.Client.Username)
@@ -148,29 +149,29 @@ func TestConfigFunctions(t *testing.T) {
148149
// ERROR PATH: Test secure config with invalid version
149150
os.Setenv("SECURE_INTEGRATION", "true")
150151
os.Setenv("OPENSEARCH_VERSION", "not.a.version")
151-
config, err = ClientConfig()
152+
config, err = ostest.ClientConfig()
152153
assert.Error(t, err, "Should propagate GetPassword error")
153154
assert.Nil(t, config, "Config should be nil on error")
154155
})
155156
}
156157

157158
// TestHelperFunctions tests utility functions with different scenarios
158159
func TestHelperFunctions(t *testing.T) {
159-
client, err := NewClient(t)
160+
client, err := ostest.NewClient(t)
160161
require.NoError(t, err)
161162

162-
// Test SkipIfBelowVersion - this will not skip since we're running on 3.4.0
163-
t.Run("SkipIfBelowVersion", func(t *testing.T) {
163+
// Test ostest.SkipIfBelowVersion - this will not skip since we're running on 3.4.0
164+
t.Run("ostest.SkipIfBelowVersion", func(t *testing.T) {
164165
// This should not skip on current version (3.4.0+)
165-
SkipIfBelowVersion(t, client, 2, 0, "TestFeature")
166+
ostest.SkipIfBelowVersion(t, client, 2, 0, "TestFeature")
166167
// If we reach here, the test didn't skip
167168
assert.True(t, true, "Test should continue for supported version")
168169
})
169170

170171
// Test SkipIfNotSecure
171172
t.Run("SkipIfNotSecure", func(t *testing.T) {
172-
if IsSecure() {
173-
SkipIfNotSecure(t)
173+
if ostest.IsSecure() {
174+
ostest.SkipIfNotSecure(t)
174175
// If we reach here, it's a secure cluster and test continued
175176
assert.True(t, true, "Test should continue for secure cluster")
176177
}
@@ -184,27 +185,27 @@ func TestHelperFunctions(t *testing.T) {
184185
require.NoError(t, err)
185186

186187
// This tests the JSON comparison utility
187-
CompareRawJSONwithParsedJSON(t, resp, resp.Inspect().Response)
188+
ostest.CompareRawJSONwithParsedJSON(t, resp, resp.Inspect().Response)
188189
})
189190

190191
// Test GetVersion function
191192
t.Run("GetVersion", func(t *testing.T) {
192193
// Test successful version retrieval
193-
major, minor, patch, err := GetVersion(client, t)
194+
major, minor, patch, err := ostest.GetVersion(t, client)
194195
assert.NoError(t, err, "GetVersion should succeed with valid client")
195196
assert.True(t, major >= 1, "Major version should be at least 1")
196197
assert.True(t, minor >= 0, "Minor version should be non-negative")
197198
assert.True(t, patch >= 0, "Patch version should be non-negative")
198199

199200
// ERROR PATH: Test GetVersion with nil client
200-
_, _, _, err = GetVersion(nil, t)
201+
_, _, _, err = ostest.GetVersion(t, nil)
201202
assert.Error(t, err, "GetVersion should error with nil client")
202203
})
203204

204205
// Test NewClient function
205206
t.Run("NewClient", func(t *testing.T) {
206207
// Test successful client creation (already done above)
207-
client, err := NewClient(t)
208+
client, err := ostest.NewClient(t)
208209
assert.NoError(t, err, "NewClient should succeed in normal conditions")
209210
assert.NotNil(t, client, "Client should not be nil")
210211

@@ -229,30 +230,14 @@ func TestHelperFunctions(t *testing.T) {
229230
os.Setenv("SECURE_INTEGRATION", "true")
230231
os.Setenv("OPENSEARCH_VERSION", "invalid.format")
231232

232-
_, err = NewClient(t)
233+
_, err = ostest.NewClient(t)
233234
assert.Error(t, err, "NewClient should propagate ClientConfig errors")
234235
})
235-
236-
// Test extendedReadinessCheck function
237-
t.Run("extendedReadinessCheck", func(t *testing.T) {
238-
// Test successful validation (using working client)
239-
ctx := context.Background()
240-
err := extendedReadinessCheck(ctx, client)
241-
assert.NoError(t, err, "extendedReadinessCheck should succeed with healthy cluster")
242-
243-
// ERROR PATH: Test with cancelled context
244-
cancelledCtx, cancel := context.WithCancel(context.Background())
245-
cancel() // Cancel immediately
246-
247-
err = extendedReadinessCheck(cancelledCtx, client)
248-
assert.Error(t, err, "extendedReadinessCheck should error with cancelled context")
249-
assert.Contains(t, err.Error(), "failed", "Error should contain descriptive message")
250-
})
251236
}
252237

253238
// ExampleTestSuite demonstrates using the testify suite pattern
254239
type ExampleTestSuite struct {
255-
OpenSearchTestSuite
240+
ostest.OpenSearchTestSuite
256241
}
257242

258243
func (s *ExampleTestSuite) TestClusterHealthWithSuite() {
@@ -300,7 +285,7 @@ func (s *ExampleTestSuite) TestVersionSkipping() {
300285

301286
// Test secure cluster skipping if not secure
302287
func (s *ExampleTestSuite) TestSecureSkipping() {
303-
if !IsSecure() {
288+
if !ostest.IsSecure() {
304289
s.SkipIfNotSecure()
305290
// This line should not be reached for insecure clusters
306291
s.T().Error("This test should have been skipped for insecure cluster")

internal/test/suite.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (s *OpenSearchTestSuite) SetupSuite() {
3636
s.Client = client
3737

3838
// Get and store version information for test use
39-
major, minor, patch, err := GetVersion(s.Client, t)
39+
major, minor, patch, err := GetVersion(t, s.Client)
4040
require.NoError(t, err, "Failed to get OpenSearch version")
4141

4242
s.Major, s.Minor, s.Patch = major, minor, patch

opensearch.go

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ type Config struct {
100100
Logger opensearchtransport.Logger // The logger object.
101101
Selector opensearchtransport.Selector // The selector object.
102102

103+
// Context for background operations. If nil, context.Background() will be used.
104+
// This is only used during client initialization and is not stored long-term.
105+
//nolint:containedctx // Config struct is short-lived, context extracted during New()
106+
Context context.Context
107+
CancelFunc context.CancelFunc
108+
103109
// Optional constructor function for a custom ConnectionPool. Default: nil.
104110
ConnectionPoolFunc func([]*opensearchtransport.Connection, opensearchtransport.Selector) opensearchtransport.ConnectionPool
105111
}
@@ -139,6 +145,11 @@ func NewClient(cfg Config) (*Client, error) {
139145
addrs = append(addrs, cfg.Addresses...)
140146
}
141147

148+
// Initialize context if not provided
149+
if cfg.Context == nil {
150+
cfg.Context, cfg.CancelFunc = context.WithCancel(context.Background())
151+
}
152+
142153
urls, err := addrsToURLs(addrs)
143154
if err != nil {
144155
return nil, fmt.Errorf("%w: %w", ErrCreateClient, err)
@@ -148,24 +159,11 @@ func NewClient(cfg Config) (*Client, error) {
148159
//nolint:errcheck // errcheck exclude ???
149160
u, _ := url.Parse(defaultURL)
150161
urls = append(urls, u)
151-
} else if cfg.Username == "" || cfg.Password == "" {
152-
// Extract credentials from the first URL that has them (only if not already configured)
153-
for _, u := range urls {
154-
if u.User != nil {
155-
if cfg.Username == "" {
156-
cfg.Username = u.User.Username()
157-
}
158-
if cfg.Password == "" {
159-
if pw, ok := u.User.Password(); ok {
160-
cfg.Password = pw
161-
}
162-
}
163-
// Stop after finding the first URL with credentials
164-
break
165-
}
166-
}
167162
}
168163

164+
// Extract credentials from the first URL that has them (only if not already configured)
165+
extractCredentialsFromURLs(&cfg, urls)
166+
169167
tp, err := opensearchtransport.New(opensearchtransport.Config{
170168
URLs: urls,
171169
Username: cfg.Username,
@@ -193,6 +191,8 @@ func NewClient(cfg Config) (*Client, error) {
193191
Logger: cfg.Logger,
194192
Selector: cfg.Selector,
195193
ConnectionPoolFunc: cfg.ConnectionPoolFunc,
194+
Context: cfg.Context,
195+
CancelFunc: cfg.CancelFunc,
196196
})
197197
if err != nil {
198198
return nil, fmt.Errorf("%w: %w", ErrCreateTransport, err)
@@ -201,8 +201,15 @@ func NewClient(cfg Config) (*Client, error) {
201201
client := &Client{Transport: tp}
202202

203203
if cfg.DiscoverNodesOnStart {
204-
//nolint:errcheck // goroutine discards return values
205-
go client.DiscoverNodes()
204+
go func() {
205+
start := time.Now()
206+
if err := client.DiscoverNodes(cfg.Context); err != nil {
207+
if cfg.Logger != nil {
208+
//nolint:errcheck // Logger errors are not critical for discovery
209+
cfg.Logger.LogRoundTrip(nil, nil, err, start, time.Since(start))
210+
}
211+
}
212+
}()
206213
}
207214

208215
return client, err
@@ -294,9 +301,9 @@ func (c *Client) Metrics() (opensearchtransport.Metrics, error) {
294301
}
295302

296303
// DiscoverNodes reloads the client connections by fetching information from the cluster.
297-
func (c *Client) DiscoverNodes() error {
304+
func (c *Client) DiscoverNodes(ctx context.Context) error {
298305
if dt, ok := c.Transport.(opensearchtransport.Discoverable); ok {
299-
return dt.DiscoverNodes()
306+
return dt.DiscoverNodes(ctx)
300307
}
301308

302309
return ErrTransportMissingMethodDiscoverNodes
@@ -335,6 +342,31 @@ func addrsToURLs(addrs []string) ([]*url.URL, error) {
335342
return urls, nil
336343
}
337344

345+
// extractCredentialsFromURLs extracts username and password from the first URL that has them.
346+
// Only extracts credentials that are not already configured in cfg.
347+
func extractCredentialsFromURLs(cfg *Config, urls []*url.URL) {
348+
if len(urls) == 0 || (cfg.Username != "" && cfg.Password != "") {
349+
return // No URLs or credentials already fully configured
350+
}
351+
352+
for _, u := range urls {
353+
if u.User == nil {
354+
continue
355+
}
356+
357+
if cfg.Username == "" {
358+
cfg.Username = u.User.Username()
359+
}
360+
if cfg.Password == "" {
361+
if pw, ok := u.User.Password(); ok {
362+
cfg.Password = pw
363+
}
364+
}
365+
// Stop after finding the first URL with credentials
366+
break
367+
}
368+
}
369+
338370
// ToPointer converts any value to a pointer, mainly used for request parameters
339371
func ToPointer[V any](value V) *V {
340372
return &value

0 commit comments

Comments
 (0)