Skip to content

poc for fileconfiguration failover#55

Merged
cristiGuranIonos merged 15 commits intov2from
feat/poc_config_failover
Mar 6, 2026
Merged

poc for fileconfiguration failover#55
cristiGuranIonos merged 15 commits intov2from
feat/poc_config_failover

Conversation

@cristiGuranIonos
Copy link
Collaborator

No description provided.

cristiGuranIonos and others added 6 commits February 25, 2026 10:16
)

* refactor callAPI to be a wrapper function, so behaviour can actually be tested

* tests, comments, refactor

* do not retry if context is canceled
* fix all issues caught by sonarqube

* error on non-existing failoverStrategy, refactor doFailoverAttempt, better logging

* simplify doFailoverAttempt, check for NetworkError in orderedRoundTrip
@cristiGuranIonos cristiGuranIonos marked this pull request as ready for review March 5, 2026 10:45
@cristiGuranIonos cristiGuranIonos requested a review from Copilot March 5, 2026 11:44
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces transport-level endpoint failover (RoundTripper-based) and extracts the per-product HTTP retry logic into a shared shared/retry package, with initial YAML deserialization support for failover options in shared/fileconfiguration.

Changes:

  • Add shared/failover RoundTripper with RoundRobin strategy, backoff, and extensive unit/E2E tests.
  • Centralize application-level HTTP retry into shared/retry.DoWithApplicationRetry and switch compute client to use it.
  • Extend file configuration to deserialize a top-level failover: block into failover.Options.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
shared/retry/retry.go New shared application-level retry implementation used by product clients.
shared/logger.go Adds LogDebug/LogTrace helpers for log-level-gated logging.
shared/go.mod Adds dependencies needed by failover/retry + (currently) compute for E2E test.
shared/go.sum Updates checksums for newly required modules.
shared/fileconfiguration/fileconfiguration.go Adds Failover *failover.Options field and accessor.
shared/fileconfiguration/fileconfiguration_test.go Tests YAML deserialization and nil behavior for failover options.
shared/failover/helpers_test.go Test helpers for RoundTripper/network error simulation and backoff.
shared/failover/failover_roundtripper.go New failover RoundTripper implementation and option types.
shared/failover/failover_roundtripper_test.go Unit tests covering failover behavior across error/status scenarios.
shared/failover/failover_e2e_test.go E2E test wiring compute client to failover transport via shared module.
shared/configuration.go Exports DefaultMaxRetries and updates default initializations.
products/compute/go.mod Adds local replace to shared (duplicated) and indirect deps for failover tests.
products/compute/go.sum Updates module checksums for new deps.
products/compute/client.go Replaces inline retry logic with shared/retry.DoWithApplicationRetry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cristiGuranIonos and others added 4 commits March 5, 2026 13:57
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

logRequest(clonedRequest, 0)

httpRequestStartTime := time.Now()
clonedRequest.Close = true
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This retry implementation sets clonedRequest.Close = true, which disables connection reuse, but later drains the response body “so the connection can be reused”. Either remove Close = true to allow keep-alives (and keep draining), or drop the drain logic if connections are intentionally closed each attempt.

Suggested change
clonedRequest.Close = true

Copilot uses AI. Check for mistakes.
if !shared.SdkLogLevel.Satisfies(shared.Trace) {
logReq.Header.Del("Authorization")
}
dump, err := httputil.DumpRequestOut(logReq, true)
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request.Clone() shares the same Body reader with the original request. httputil.DumpRequestOut(..., true) reads the body; with the current flow (logging the same cloned request you later send), debug logging can consume the body and cause the real request to be sent with an empty/truncated payload. To avoid this, ensure the request being dumped has an independent body reader (e.g., set logReq.Body from request.GetBody() when available) rather than reusing the same Body instance.

Suggested change
dump, err := httputil.DumpRequestOut(logReq, true)
dumpBody := false
if request.GetBody != nil {
if bodyCopy, err := request.GetBody(); err == nil && bodyCopy != nil {
defer bodyCopy.Close()
logReq.Body = bodyCopy
dumpBody = true
}
}
dump, err := httputil.DumpRequestOut(logReq, dumpBody)

Copilot uses AI. Check for mistakes.
Comment on lines +439 to +440
// MaxRetries controls how many times the transport will attempt a request using the set strategy before giving up
// and returning the last error. If zero, it defaults to 3.
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Options.MaxRetries docs are inconsistent with the implementation: orderedRoundTrip uses totalAttempts := maxRetries + 1, but Options.MaxRetries is described as “how many times the transport will attempt a request”. Please clarify whether MaxRetries is “retries” or “attempts” and align the comment (and the worst-case attempts formula) accordingly.

Suggested change
// MaxRetries controls how many times the transport will attempt a request using the set strategy before giving up
// and returning the last error. If zero, it defaults to 3.
// MaxRetries controls how many times the transport will retry a request using the set strategy
// after the initial attempt before giving up and returning the last error. In other words, the
// worst-case total number of attempts is MaxRetries+1. If zero, it defaults to 3.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +18

replace github.com/ionos-cloud/sdk-go-bundle/shared => ../../shared
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate replace github.com/ionos-cloud/sdk-go-bundle/shared => ../../shared: this file declares it twice, which will make go mod tidy fail. Remove one of the duplicate replace directives.

Suggested change
replace github.com/ionos-cloud/sdk-go-bundle/shared => ../../shared

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +23
// DoWithApplicationRetry executes an HTTP request with application-level retry
// logic. This is the shared equivalent of the per-product callAPI method.
//
// Retry behaviour:
// - 502/503/504: retries with backoff (skips POST)
// - 429: retries honoring Retry-After header, falls back to waitTime
// - Other status codes or transport errors: returns immediately
// - Respects context cancellation during backoff
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR introduces a new shared retry helper, but there are no unit tests covering the key behaviors (e.g., 429 Retry-After parsing, 502/503/504 retry rules, and POST not being retried). Please add focused tests under shared/retry to protect future migrations from regressions.

Copilot uses AI. Check for mistakes.
) (*http.Response, time.Duration, error) {
if cfg == nil {
return nil, 0, errors.New("nil configuration")
}
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DoWithApplicationRetry validates cfg but not request. A nil request will panic when accessing request.Method / request.Context(). Add an early request == nil check and return a clear error.

Suggested change
}
}
if request == nil {
return nil, 0, errors.New("nil request")
}

Copilot uses AI. Check for mistakes.
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 6, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
1 New Critical Issues (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@cristiGuranIonos cristiGuranIonos merged commit 4376df3 into v2 Mar 6, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants