Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Retry Policy #521

Merged
merged 23 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f8480ad
Implemented Retry Policy
4gust Oct 21, 2024
10df4c9
Fixed the tests
4gust Oct 22, 2024
1782b07
Updated the retry policy to respect context
4gust Oct 23, 2024
a60fd6d
Updated the variable name to remove negation
4gust Oct 24, 2024
716cbce
Update managedidentity.go
4gust Oct 29, 2024
8319e22
Update managedidentity_test.go
4gust Oct 29, 2024
31fc7a0
Update managedidentity.go
4gust Nov 1, 2024
54ff161
Update managedidentity.go
4gust Nov 6, 2024
4e2ed03
Added a context exit for request.
4gust Nov 7, 2024
f4bbc08
Merge branch 'andyohart/managed-identity' of https://github.com/Azure…
4gust Nov 13, 2024
c225221
Added a source based retry
4gust Nov 15, 2024
e5c8bc7
Updated status code list.
4gust Nov 15, 2024
4f3b414
Updated comment.
4gust Nov 15, 2024
191e9c2
Merge branch 'andyohart/managed-identity' of https://github.com/Azure…
4gust Nov 18, 2024
abf8b86
Updated retry code logic.
4gust Nov 19, 2024
1cd9908
Update apps/managedidentity/managedidentity.go
4gust Nov 21, 2024
73fda09
updated code for comments
4gust Nov 21, 2024
33386f0
Merge branch '4gust/mi-retry-policy' of https://github.com/AzureAD/mi…
4gust Nov 21, 2024
23290c4
Updated tests
4gust Nov 25, 2024
a81a5be
Update apps/managedidentity/managedidentity_test.go
4gust Nov 26, 2024
86d023d
updated tests and comments
4gust Nov 26, 2024
d93e752
Merge branch '4gust/mi-retry-policy' of https://github.com/AzureAD/mi…
4gust Nov 26, 2024
3fd8ad5
Update managedidentity_test.go
4gust Nov 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 100 additions & 11 deletions apps/managedidentity/managedidentity.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"path/filepath"
"runtime"
"strings"
"time"

"github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base"
Expand Down Expand Up @@ -72,8 +73,39 @@ const (
imdsEndVar = "IMDS_ENDPOINT"
msiEndpointEnvVar = "MSI_ENDPOINT"
identityServerThumbprintEnvVar = "IDENTITY_SERVER_THUMBPRINT"

defaultRetryCount = 3
)

// retry codes for IMDS
4gust marked this conversation as resolved.
Show resolved Hide resolved
var retryCodesForIMDS = []int{
4gust marked this conversation as resolved.
Show resolved Hide resolved
http.StatusNotFound, // 404
http.StatusRequestTimeout, // 408
4gust marked this conversation as resolved.
Show resolved Hide resolved
http.StatusGone, // 410
http.StatusTooManyRequests, // 429
http.StatusInternalServerError, // 500
http.StatusNotImplemented, // 501
http.StatusBadGateway, // 502
http.StatusServiceUnavailable, // 503
http.StatusGatewayTimeout, // 504
http.StatusHTTPVersionNotSupported, // 505
http.StatusVariantAlsoNegotiates, // 506
http.StatusInsufficientStorage, // 507
http.StatusLoopDetected, // 508
http.StatusNotExtended, // 510
http.StatusNetworkAuthenticationRequired, // 511
}

// retry on these codes
4gust marked this conversation as resolved.
Show resolved Hide resolved
var retryStatusCodes = []int{
bgavrilMS marked this conversation as resolved.
Show resolved Hide resolved
http.StatusNotFound, // 404
4gust marked this conversation as resolved.
Show resolved Hide resolved
http.StatusRequestTimeout, // 408
http.StatusTooManyRequests, // 429
http.StatusInternalServerError, // 500
http.StatusServiceUnavailable, // 503
4gust marked this conversation as resolved.
Show resolved Hide resolved
http.StatusGatewayTimeout, // 504
}

var getAzureArcPlatformPath = func(platform string) string {
switch platform {
case "windows":
Expand Down Expand Up @@ -119,14 +151,16 @@ func SystemAssigned() ID {
var cacheManager *storage.Manager = storage.New(nil)

type Client struct {
httpClient ops.HTTPClient
miType ID
source Source
authParams authority.AuthParams
httpClient ops.HTTPClient
miType ID
source Source
authParams authority.AuthParams
retryPolicyEnabled bool
}

type ClientOptions struct {
httpClient ops.HTTPClient
httpClient ops.HTTPClient
retryPolicyEnabled bool
}

type AcquireTokenOptions struct {
Expand All @@ -152,6 +186,12 @@ func WithHTTPClient(httpClient ops.HTTPClient) ClientOption {
}
}

func WithRetryPolicyDisabled() ClientOption {
return func(o *ClientOptions) {
o.retryPolicyEnabled = false
}
}

// Client to be used to acquire tokens for managed identity.
// ID: [SystemAssigned], [UserAssignedClientID], [UserAssignedResourceID], [UserAssignedObjectID]
//
Expand All @@ -170,7 +210,8 @@ func New(id ID, options ...ClientOption) (Client, error) {
}
}
opts := ClientOptions{
httpClient: shared.DefaultClient,
httpClient: shared.DefaultClient,
retryPolicyEnabled: true,
}
for _, option := range options {
option(&opts)
Expand All @@ -193,9 +234,10 @@ func New(id ID, options ...ClientOption) (Client, error) {
return Client{}, fmt.Errorf("unsupported type %T", id)
}
client := Client{
miType: id,
httpClient: opts.httpClient,
source: source,
miType: id,
httpClient: opts.httpClient,
retryPolicyEnabled: opts.retryPolicyEnabled,
source: source,
}
fakeAuthInfo, err := authority.NewInfoFromAuthorityURI("https://login.microsoftonline.com/managed_identity", false, true)
if err != nil {
Expand Down Expand Up @@ -323,9 +365,56 @@ func authResultFromToken(authParams authority.AuthParams, token accesstokens.Tok
return ar, err
}

// Contains checks if the element is present in the list.
4gust marked this conversation as resolved.
Show resolved Hide resolved
func contains[T comparable](list []T, element T) bool {
for _, v := range list {
if v == element {
return true
}
}
return false
}

// retry performs an HTTP request with retries based on the provided options.
func retry(maxRetries int, c ops.HTTPClient, req *http.Request, s Source) (*http.Response, error) {
4gust marked this conversation as resolved.
Show resolved Hide resolved
var resp *http.Response
var err error
for attempt := 0; attempt < maxRetries; attempt++ {
tryCtx, tryCancel := context.WithTimeout(req.Context(), time.Second*15)
4gust marked this conversation as resolved.
Show resolved Hide resolved
defer tryCancel()
cloneReq := req.Clone(tryCtx)
resp, err = c.Do(cloneReq)
retrylist := retryStatusCodes
if s == DefaultToIMDS {
retrylist = retryCodesForIMDS
}
if err == nil && !contains(retrylist, resp.StatusCode) {
return resp, nil
}
if attempt == maxRetries-1 {
4gust marked this conversation as resolved.
Show resolved Hide resolved
return resp, err
}
if resp != nil && resp.Body != nil {
io.Copy(io.Discard, resp.Body)
4gust marked this conversation as resolved.
Show resolved Hide resolved
resp.Body.Close()
}
select {
case <-time.After(time.Second):
case <-req.Context().Done():
err = req.Context().Err()
return resp, err
}
}
return resp, err
}

func (client Client) getTokenForRequest(req *http.Request) (accesstokens.TokenResponse, error) {
var r accesstokens.TokenResponse
resp, err := client.httpClient.Do(req)
r := accesstokens.TokenResponse{}
retryCount := defaultRetryCount
if !client.retryPolicyEnabled {
retryCount = 1
}
resp, err := retry(retryCount, client.httpClient, req, client.source)
if err != nil {
return r, err
}
Expand Down
Loading