Skip to content

Commit 68bdce8

Browse files
committed
refactor(aws/services): extract findOfferingID helpers to fit gocyclo budget
memorydb: extract scanMemoryDBOfferingPage (per-page offering scan with mismatch guard) and isLastMemoryDBPage (terminal-page check) from findOfferingID, mirroring the helpers already in the ec2 package. ec2: extract buildEC2QueryFromRec (details type-assertion + payment-option conversion + query assembly) from findOfferingID into a standalone method. Both findOfferingID functions now sit at cyclomatic complexity ≤10. No behaviour change -- pure extraction verified by 72 passing unit tests.
1 parent b2011b5 commit 68bdce8

2 files changed

Lines changed: 59 additions & 22 deletions

File tree

‎providers/aws/services/ec2/client.go‎

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,24 @@ func describeInputFromQuery(q ec2OfferingQuery, nextToken *string) *ec2.Describe
393393
}
394394
}
395395

396+
// buildEC2QueryFromRec extracts ComputeDetails from rec, converts the payment
397+
// option, and assembles the ec2OfferingQuery used by findOfferingID.
398+
//
399+
// Pulled out of findOfferingID to keep that function under the cyclomatic limit.
400+
func (c *Client) buildEC2QueryFromRec(rec common.Recommendation) (ec2OfferingQuery, error) {
401+
details, ok := rec.Details.(*common.ComputeDetails)
402+
if !ok || details == nil {
403+
return ec2OfferingQuery{}, fmt.Errorf("invalid service details for EC2")
404+
}
405+
wantOfferingType, err := convertEC2PaymentOption(rec.PaymentOption)
406+
if err != nil {
407+
return ec2OfferingQuery{}, err
408+
}
409+
q := buildEC2OfferingQuery(rec, details, c.getDurationValue(rec.Term))
410+
q.wantOfferingType = wantOfferingType
411+
return q, nil
412+
}
413+
396414
// findOfferingID finds the appropriate EC2 Reserved Instance offering ID.
397415
//
398416
// The input is built from typed first-class fields on
@@ -407,24 +425,18 @@ func describeInputFromQuery(q ec2OfferingQuery, nextToken *string) *ec2.Describe
407425
// execID is the purchase execution UUID for log correlation; pass "" when
408426
// calling outside of a purchase flow (ValidateOffering, GetOfferingDetails).
409427
func (c *Client) findOfferingID(ctx context.Context, rec common.Recommendation, execID string) (string, error) {
410-
details, ok := rec.Details.(*common.ComputeDetails)
411-
if !ok || details == nil {
412-
return "", fmt.Errorf("invalid service details for EC2")
413-
}
414-
wantOfferingType, err := convertEC2PaymentOption(rec.PaymentOption)
428+
q, err := c.buildEC2QueryFromRec(rec)
415429
if err != nil {
416430
return "", err
417431
}
418-
q := buildEC2OfferingQuery(rec, details, c.getDurationValue(rec.Term))
419-
q.wantOfferingType = wantOfferingType
420432

421433
tag := execID
422434
if tag == "" {
423435
tag = "no-exec"
424436
}
425437
t0 := time.Now()
426438
log.Printf("purchase[%s]: EC2 findOfferingID starting (instance=%s platform=%s tenancy=%s term=%s payment=%s)",
427-
tag, rec.ResourceType, details.Platform, details.Tenancy, rec.Term, rec.PaymentOption)
439+
tag, rec.ResourceType, q.productDesc, q.tenancy, rec.Term, rec.PaymentOption)
428440

429441
var nextToken *string
430442
page := 0
@@ -435,7 +447,7 @@ func (c *Client) findOfferingID(ctx context.Context, rec common.Recommendation,
435447
page++
436448
if page > maxOfferingPages {
437449
return "", fmt.Errorf("pagination cap reached after %d pages for EC2 %s %s %s (issue #688)",
438-
maxOfferingPages, rec.ResourceType, details.Platform, rec.PaymentOption)
450+
maxOfferingPages, rec.ResourceType, q.productDesc, rec.PaymentOption)
439451
}
440452
pageStart := time.Now()
441453
result, err := c.client.DescribeReservedInstancesOfferings(ctx, describeInputFromQuery(q, nextToken))
@@ -446,7 +458,7 @@ func (c *Client) findOfferingID(ctx context.Context, rec common.Recommendation,
446458
}
447459
log.Printf("purchase[%s]: EC2 findOfferingID page %d: %d offerings in %s",
448460
tag, page, len(result.ReservedInstancesOfferings), time.Since(pageStart))
449-
if id := scanEC2OfferingPage(result.ReservedInstancesOfferings, wantOfferingType); id != "" {
461+
if id := scanEC2OfferingPage(result.ReservedInstancesOfferings, q.wantOfferingType); id != "" {
450462
log.Printf("purchase[%s]: EC2 findOfferingID found match on page %d after %s total",
451463
tag, page, time.Since(t0))
452464
return id, nil
@@ -459,7 +471,7 @@ func (c *Client) findOfferingID(ctx context.Context, rec common.Recommendation,
459471
log.Printf("purchase[%s]: EC2 findOfferingID exhausted %d page(s) in %s -- no match",
460472
tag, page, time.Since(t0))
461473
return "", fmt.Errorf("no offerings found for EC2 %s %s %s after %d page(s) (issue #688)",
462-
rec.ResourceType, details.Platform, rec.PaymentOption, page)
474+
rec.ResourceType, q.productDesc, rec.PaymentOption, page)
463475
}
464476

465477
// isLastEC2Page reports whether a NextToken indicates the terminal page.

‎providers/aws/services/memorydb/client.go‎

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -328,19 +328,13 @@ func (c *Client) findOfferingID(ctx context.Context, rec common.Recommendation,
328328
log.Printf("purchase[%s]: MemoryDB findOfferingID page %d: %d offerings in %s",
329329
tag, page, len(result.ReservedNodesOfferings), time.Since(pageStart))
330330

331-
for _, o := range result.ReservedNodesOfferings {
332-
got := aws.ToString(o.OfferingType)
333-
if got != wantOfferingType {
334-
return "", fmt.Errorf("MemoryDB offering %s has payment option %q, want %q (rec: %s %s) -- API filter mismatch",
335-
aws.ToString(o.ReservedNodesOfferingId), got, wantOfferingType,
336-
rec.ResourceType, rec.PaymentOption)
337-
}
338-
log.Printf("purchase[%s]: MemoryDB findOfferingID found match on page %d after %s total",
339-
tag, page, time.Since(t0))
340-
return aws.ToString(o.ReservedNodesOfferingId), nil
331+
if id, err := scanMemoryDBOfferingPage(result.ReservedNodesOfferings, wantOfferingType, rec, tag, page, t0); err != nil {
332+
return "", err
333+
} else if id != "" {
334+
return id, nil
341335
}
342336

343-
if result.NextToken == nil || aws.ToString(result.NextToken) == "" {
337+
if isLastMemoryDBPage(result.NextToken) {
344338
break
345339
}
346340
nextToken = result.NextToken
@@ -352,6 +346,37 @@ func (c *Client) findOfferingID(ctx context.Context, rec common.Recommendation,
352346
rec.ResourceType, rec.PaymentOption, page)
353347
}
354348

349+
// scanMemoryDBOfferingPage inspects a single page of DescribeReservedNodesOfferings
350+
// results and returns the first matching offering ID.
351+
// It returns ("", nil) when the page is empty or no offering matched; it returns
352+
// ("", err) on an API filter mismatch; it returns (id, nil) on a successful match.
353+
//
354+
// Pulled out of findOfferingID to keep that function under the cyclomatic limit.
355+
func scanMemoryDBOfferingPage(offerings []types.ReservedNodesOffering, wantOfferingType string, rec common.Recommendation, tag string, page int, t0 time.Time) (string, error) {
356+
for _, o := range offerings {
357+
got := aws.ToString(o.OfferingType)
358+
if got != wantOfferingType {
359+
return "", fmt.Errorf("MemoryDB offering %s has payment option %q, want %q (rec: %s %s) -- API filter mismatch",
360+
aws.ToString(o.ReservedNodesOfferingId), got, wantOfferingType,
361+
rec.ResourceType, rec.PaymentOption)
362+
}
363+
log.Printf("purchase[%s]: MemoryDB findOfferingID found match on page %d after %s total",
364+
tag, page, time.Since(t0))
365+
return aws.ToString(o.ReservedNodesOfferingId), nil
366+
}
367+
return "", nil
368+
}
369+
370+
// isLastMemoryDBPage reports whether a NextToken indicates the terminal page.
371+
// The AWS SDK may return either nil or a pointer to an empty string for the
372+
// last page; both must end pagination so the loop does not issue a redundant
373+
// request (and risk a false page-cap error on borderline page counts).
374+
//
375+
// Pulled out of findOfferingID to keep that function under the cyclomatic limit.
376+
func isLastMemoryDBPage(nextToken *string) bool {
377+
return nextToken == nil || aws.ToString(nextToken) == ""
378+
}
379+
355380
// getDurationStringForAPI converts the term string to a duration value accepted
356381
// by DescribeReservedNodesOfferings (seconds as a string or "1yr"/"3yr").
357382
// The MemoryDB API accepts both numeric-seconds strings and year strings.

0 commit comments

Comments
 (0)