|
4 | 4 | package pusher
|
5 | 5 |
|
6 | 6 | import (
|
| 7 | + "fmt" |
7 | 8 | "sync"
|
8 | 9 | "sync/atomic"
|
9 | 10 | "testing"
|
@@ -120,97 +121,6 @@ func TestTargetManager(t *testing.T) {
|
120 | 121 | assertCacheLen(t, manager, 0)
|
121 | 122 | })
|
122 | 123 |
|
123 |
| - t.Run("SetRetentionPolicy", func(t *testing.T) { |
124 |
| - target := Target{Group: "G", Stream: "S", Retention: 7} |
125 |
| - |
126 |
| - mockService := new(mockLogsService) |
127 |
| - mockService.On("CreateLogStream", mock.Anything).Return(&cloudwatchlogs.CreateLogStreamOutput{}, nil).Once() |
128 |
| - mockService.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
129 |
| - LogGroups: []*cloudwatchlogs.LogGroup{ |
130 |
| - { |
131 |
| - LogGroupName: aws.String(target.Group), |
132 |
| - RetentionInDays: aws.Int64(0), |
133 |
| - }, |
134 |
| - }, |
135 |
| - }, nil).Once() |
136 |
| - mockService.On("PutRetentionPolicy", mock.Anything).Return(&cloudwatchlogs.PutRetentionPolicyOutput{}, nil).Once() |
137 |
| - |
138 |
| - manager := NewTargetManager(logger, mockService) |
139 |
| - err := manager.InitTarget(target) |
140 |
| - assert.NoError(t, err) |
141 |
| - // Wait for async operations to complete |
142 |
| - time.Sleep(100 * time.Millisecond) |
143 |
| - mockService.AssertExpectations(t) |
144 |
| - assertCacheLen(t, manager, 1) |
145 |
| - }) |
146 |
| - |
147 |
| - t.Run("SetRetentionPolicy/NoChange", func(t *testing.T) { |
148 |
| - target := Target{Group: "G", Stream: "S", Retention: 7} |
149 |
| - |
150 |
| - mockService := new(mockLogsService) |
151 |
| - mockService.On("CreateLogStream", mock.Anything).Return(&cloudwatchlogs.CreateLogStreamOutput{}, nil).Once() |
152 |
| - mockService.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
153 |
| - LogGroups: []*cloudwatchlogs.LogGroup{ |
154 |
| - { |
155 |
| - LogGroupName: aws.String(target.Group), |
156 |
| - RetentionInDays: aws.Int64(7), |
157 |
| - }, |
158 |
| - }, |
159 |
| - }, nil).Once() |
160 |
| - |
161 |
| - manager := NewTargetManager(logger, mockService) |
162 |
| - err := manager.InitTarget(target) |
163 |
| - assert.NoError(t, err) |
164 |
| - time.Sleep(100 * time.Millisecond) |
165 |
| - mockService.AssertExpectations(t) |
166 |
| - mockService.AssertNotCalled(t, "PutRetentionPolicy") |
167 |
| - assertCacheLen(t, manager, 1) |
168 |
| - }) |
169 |
| - |
170 |
| - t.Run("SetRetentionPolicy/LogGroupNotFound", func(t *testing.T) { |
171 |
| - t.Parallel() |
172 |
| - target := Target{Group: "G", Stream: "S", Retention: 7} |
173 |
| - |
174 |
| - mockService := new(mockLogsService) |
175 |
| - mockService.On("CreateLogStream", mock.Anything).Return(&cloudwatchlogs.CreateLogStreamOutput{}, nil).Once() |
176 |
| - mockService.On("DescribeLogGroups", mock.Anything). |
177 |
| - Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, &cloudwatchlogs.ResourceNotFoundException{}).Times(numBackoffRetries) |
178 |
| - |
179 |
| - manager := NewTargetManager(logger, mockService) |
180 |
| - err := manager.InitTarget(target) |
181 |
| - assert.NoError(t, err) |
182 |
| - time.Sleep(30 * time.Second) |
183 |
| - mockService.AssertExpectations(t) |
184 |
| - mockService.AssertNotCalled(t, "PutRetentionPolicy") |
185 |
| - assertCacheLen(t, manager, 1) |
186 |
| - }) |
187 |
| - |
188 |
| - t.Run("SetRetentionPolicy/Error", func(t *testing.T) { |
189 |
| - t.Parallel() |
190 |
| - target := Target{Group: "G", Stream: "S", Retention: 7} |
191 |
| - |
192 |
| - mockService := new(mockLogsService) |
193 |
| - mockService.On("CreateLogStream", mock.Anything).Return(&cloudwatchlogs.CreateLogStreamOutput{}, nil).Once() |
194 |
| - mockService.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
195 |
| - LogGroups: []*cloudwatchlogs.LogGroup{ |
196 |
| - { |
197 |
| - LogGroupName: aws.String(target.Group), |
198 |
| - RetentionInDays: aws.Int64(0), |
199 |
| - }, |
200 |
| - }, |
201 |
| - }, nil).Once() |
202 |
| - mockService.On("PutRetentionPolicy", mock.Anything). |
203 |
| - Return(&cloudwatchlogs.PutRetentionPolicyOutput{}, |
204 |
| - awserr.New("SomeAWSError", "Failed to set retention policy", nil)).Times(numBackoffRetries) |
205 |
| - |
206 |
| - manager := NewTargetManager(logger, mockService) |
207 |
| - err := manager.InitTarget(target) |
208 |
| - assert.NoError(t, err) |
209 |
| - time.Sleep(30 * time.Second) |
210 |
| - mockService.AssertExpectations(t) |
211 |
| - assertCacheLen(t, manager, 1) |
212 |
| - }) |
213 |
| - |
214 | 124 | t.Run("SetRetentionPolicy/Negative", func(t *testing.T) {
|
215 | 125 | target := Target{Group: "G", Stream: "S", Retention: -1}
|
216 | 126 |
|
@@ -344,6 +254,147 @@ func TestTargetManager(t *testing.T) {
|
344 | 254 | })
|
345 | 255 | }
|
346 | 256 |
|
| 257 | +func TestDescribeLogGroupsBatching(t *testing.T) { |
| 258 | + logger := testutil.NewNopLogger() |
| 259 | + |
| 260 | + t.Run("ProcessBatchOnLimit", func(t *testing.T) { |
| 261 | + mockService := new(mockLogsService) |
| 262 | + |
| 263 | + // Setup mock to expect a batch of 50 log groups |
| 264 | + mockService.On("DescribeLogGroups", mock.MatchedBy(func(input *cloudwatchlogs.DescribeLogGroupsInput) bool { |
| 265 | + return len(input.LogGroupIdentifiers) == logGroupIdentifierLimit |
| 266 | + })).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
| 267 | + LogGroups: []*cloudwatchlogs.LogGroup{}, |
| 268 | + }, nil).Once() |
| 269 | + |
| 270 | + manager := NewTargetManager(logger, mockService) |
| 271 | + tm := manager.(*targetManager) |
| 272 | + |
| 273 | + for i := 0; i < logGroupIdentifierLimit; i++ { |
| 274 | + target := Target{ |
| 275 | + Group: fmt.Sprintf("group-%d", i), |
| 276 | + Stream: "stream", |
| 277 | + Retention: 7, |
| 278 | + } |
| 279 | + tm.dlg <- target |
| 280 | + } |
| 281 | + |
| 282 | + time.Sleep(100 * time.Millisecond) |
| 283 | + |
| 284 | + mockService.AssertExpectations(t) |
| 285 | + }) |
| 286 | + |
| 287 | + t.Run("ProcessBatchOnTimer", func(t *testing.T) { |
| 288 | + mockService := new(mockLogsService) |
| 289 | + |
| 290 | + // Setup mock to expect a batch of less than 50 log groups |
| 291 | + mockService.On("DescribeLogGroups", mock.MatchedBy(func(input *cloudwatchlogs.DescribeLogGroupsInput) bool { |
| 292 | + return len(input.LogGroupIdentifiers) == 5 |
| 293 | + })).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
| 294 | + LogGroups: []*cloudwatchlogs.LogGroup{}, |
| 295 | + }, nil).Once() |
| 296 | + |
| 297 | + manager := NewTargetManager(logger, mockService) |
| 298 | + tm := manager.(*targetManager) |
| 299 | + |
| 300 | + for i := 0; i < 5; i++ { |
| 301 | + target := Target{ |
| 302 | + Group: fmt.Sprintf("group-%d", i), |
| 303 | + Stream: "stream", |
| 304 | + Retention: 7, |
| 305 | + } |
| 306 | + tm.dlg <- target |
| 307 | + } |
| 308 | + |
| 309 | + // Wait for ticker to fire (slightly longer than 5 seconds) |
| 310 | + time.Sleep(5100 * time.Millisecond) |
| 311 | + |
| 312 | + mockService.AssertExpectations(t) |
| 313 | + }) |
| 314 | + |
| 315 | + t.Run("ProcessBatchInvalidGroups", func(t *testing.T) { |
| 316 | + mockService := new(mockLogsService) |
| 317 | + |
| 318 | + // Return empty result |
| 319 | + mockService.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
| 320 | + LogGroups: []*cloudwatchlogs.LogGroup{}, |
| 321 | + }, nil).Once() |
| 322 | + |
| 323 | + manager := NewTargetManager(logger, mockService) |
| 324 | + tm := manager.(*targetManager) |
| 325 | + |
| 326 | + batch := make(map[string]Target) |
| 327 | + batch["group-1"] = Target{Group: "group-1", Stream: "stream", Retention: 7} |
| 328 | + batch["group-2"] = Target{Group: "group-2", Stream: "stream", Retention: 7} |
| 329 | + tm.updateTargetBatch(batch) |
| 330 | + |
| 331 | + // Wait for ticker to fire (slightly longer than 5 seconds) |
| 332 | + time.Sleep(5100 * time.Millisecond) |
| 333 | + |
| 334 | + mockService.AssertNotCalled(t, "PutRetentionPolicy") |
| 335 | + }) |
| 336 | + |
| 337 | + t.Run("RetentionPolicyUpdate", func(t *testing.T) { |
| 338 | + mockService := new(mockLogsService) |
| 339 | + |
| 340 | + mockService.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
| 341 | + LogGroups: []*cloudwatchlogs.LogGroup{ |
| 342 | + { |
| 343 | + LogGroupName: aws.String("group-1"), |
| 344 | + RetentionInDays: aws.Int64(1), |
| 345 | + }, |
| 346 | + { |
| 347 | + LogGroupName: aws.String("group-2"), |
| 348 | + RetentionInDays: aws.Int64(7), |
| 349 | + }, |
| 350 | + }, |
| 351 | + }, nil).Once() |
| 352 | + |
| 353 | + // Setup mock for PutRetentionPolicy (should only be called for group-1) |
| 354 | + mockService.On("PutRetentionPolicy", mock.MatchedBy(func(input *cloudwatchlogs.PutRetentionPolicyInput) bool { |
| 355 | + return *input.LogGroupName == "group-1" && *input.RetentionInDays == 7 |
| 356 | + })).Return(&cloudwatchlogs.PutRetentionPolicyOutput{}, nil).Once() |
| 357 | + |
| 358 | + manager := NewTargetManager(logger, mockService) |
| 359 | + tm := manager.(*targetManager) |
| 360 | + |
| 361 | + // Create a batch with two targets, one needing retention update |
| 362 | + batch := make(map[string]Target) |
| 363 | + batch["group-1"] = Target{Group: "group-1", Stream: "stream", Retention: 7} |
| 364 | + batch["group-2"] = Target{Group: "group-2", Stream: "stream", Retention: 7} |
| 365 | + |
| 366 | + tm.updateTargetBatch(batch) |
| 367 | + time.Sleep(100 * time.Millisecond) |
| 368 | + |
| 369 | + mockService.AssertExpectations(t) |
| 370 | + }) |
| 371 | + |
| 372 | + t.Run("BatchRetryOnError", func(t *testing.T) { |
| 373 | + mockService := new(mockLogsService) |
| 374 | + |
| 375 | + // Setup mock to fail once then succeed |
| 376 | + mockService.On("DescribeLogGroups", mock.Anything). |
| 377 | + Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, fmt.Errorf("internal error")).Once() |
| 378 | + mockService.On("DescribeLogGroups", mock.Anything). |
| 379 | + Return(&cloudwatchlogs.DescribeLogGroupsOutput{ |
| 380 | + LogGroups: []*cloudwatchlogs.LogGroup{}, |
| 381 | + }, nil).Once() |
| 382 | + |
| 383 | + manager := NewTargetManager(logger, mockService) |
| 384 | + tm := manager.(*targetManager) |
| 385 | + |
| 386 | + // Create a batch with one target |
| 387 | + batch := make(map[string]Target) |
| 388 | + batch["group-1"] = Target{Group: "group-1", Stream: "stream", Retention: 7} |
| 389 | + |
| 390 | + tm.updateTargetBatch(batch) |
| 391 | + // Sleep enough for retry |
| 392 | + time.Sleep(2 * time.Second) |
| 393 | + |
| 394 | + mockService.AssertExpectations(t) |
| 395 | + }) |
| 396 | +} |
| 397 | + |
347 | 398 | func TestCalculateBackoff(t *testing.T) {
|
348 | 399 | manager := &targetManager{}
|
349 | 400 | // should never exceed 30sec of total wait time
|
|
0 commit comments