2222import com .google .cloud .hadoop .util .AccessTokenProvider ;
2323import com .google .cloud .hadoop .util .CredentialFactory ;
2424import dev .failsafe .Failsafe ;
25+ import dev .failsafe .FailsafeException ;
2526import dev .failsafe .RetryPolicy ;
26- import io .cdap .cdap .api .exception .ErrorCategory ;
27- import io .cdap .cdap .api .exception .ErrorCategory .ErrorCategoryEnum ;
2827import io .cdap .cdap .api .exception .ErrorType ;
29- import io .cdap .cdap .api .exception .ErrorUtils ;
30- import io .cdap .plugin .gcp .bigquery .source .BigQuerySourceConfig ;
31- import io .cdap .plugin .gcp .bigquery .util .BigQueryConstants ;
28+ import io .cdap .plugin .gcp .common .GCPErrorDetailsProviderUtil ;
3229import io .cdap .plugin .gcp .common .GCPUtils ;
3330import io .cdap .plugin .gcp .common .ServerErrorException ;
3431import org .apache .hadoop .conf .Configuration ;
4037import java .time .Duration ;
4138import java .time .Instant ;
4239import java .util .Date ;
40+ import java .util .regex .Pattern ;
4341import java .util .stream .Collectors ;
4442import java .util .stream .Stream ;
4543
@@ -52,68 +50,61 @@ public class ServiceAccountAccessTokenProvider implements AccessTokenProvider {
5250 private Configuration conf ;
5351 private GoogleCredentials credentials ;
5452 private static final Gson GSON = new Gson ();
55- private static final Logger logger = LoggerFactory .getLogger (ServiceAccountAccessTokenProvider .class );
53+ private static final Logger LOG = LoggerFactory .getLogger (ServiceAccountAccessTokenProvider .class );
5654 public static final int DEFAULT_INITIAL_RETRY_DURATION_SECONDS = 5 ;
5755 public static final int DEFAULT_MAX_RETRY_COUNT = 5 ;
5856 public static final int DEFAULT_MAX_RETRY_DURATION_SECONDS = 80 ;
59-
57+ private static final RetryPolicy <Object > RETRY_POLICY = createRetryPolicy ();
58+ private static final Pattern SERVER_ERROR_PATTERN = Pattern .compile ("Unexpected Error code 5\\ d{2} trying to get " +
59+ "security access token from Compute Engine metadata for the default service account.*" );
6060
6161 @ Override
6262 public AccessToken getAccessToken () {
63- int initialRetryDuration = DEFAULT_INITIAL_RETRY_DURATION_SECONDS ;
64- int maxRetryCount = DEFAULT_MAX_RETRY_COUNT ;
65- int maxRetryDuration = DEFAULT_MAX_RETRY_DURATION_SECONDS ;
66- logger .debug (
67- "Initializing RetryPolicy with the following configuration: MaxRetryCount: {}, InitialRetryDuration: {}s, " +
68- "MaxRetryDuration: {}s" , maxRetryCount , initialRetryDuration , maxRetryDuration );
6963 try {
70- return Failsafe .with (getRetryPolicy (initialRetryDuration , maxRetryDuration , maxRetryCount ))
71- .get (() -> {
72- com .google .auth .oauth2 .AccessToken token = safeGetAccessToken ();
64+ return Failsafe .with (RETRY_POLICY ).get (() -> {
65+ com .google .auth .oauth2 .AccessToken token = retrieveAccessToken ();
7366 if (token == null || token .getExpirationTime ().before (Date .from (Instant .now ()))) {
7467 refresh ();
75- token = safeGetAccessToken ();
68+ token = retrieveAccessToken ();
7669 }
7770 return new AccessToken (token .getTokenValue (), token .getExpirationTime ().getTime ());
7871 });
79- } catch (Exception e ) {
80- throw ErrorUtils .getProgramFailureException (
81- new ErrorCategory (ErrorCategoryEnum .PLUGIN ),
82- "Unable to get service account access token after retries." ,
83- e .getMessage (),
84- ErrorType .UNKNOWN ,
85- true ,
86- e
72+ } catch (FailsafeException e ) {
73+ Throwable t = e .getCause () != null ? e .getCause () : e ;
74+ ErrorType errorType = (t instanceof ServerErrorException ) ? ErrorType .SYSTEM : ErrorType .UNKNOWN ;
75+ throw GCPErrorDetailsProviderUtil .getHttpResponseExceptionDetailsFromChain (
76+ e , "Unable to get service account access token after retries." , errorType , true ,
77+ GCPUtils .GCE_METADATA_SERVER_ERROR_SUPPORTED_DOC_URL
8778 );
8879 }
8980 }
9081
91-
92- private RetryPolicy <Object > getRetryPolicy (int initialRetryDuration , int maxRetryDuration ,
93- int maxRetryCount ) {
82+ private static RetryPolicy <Object > createRetryPolicy () {
9483 return RetryPolicy .builder ()
9584 .handle (ServerErrorException .class )
96- .withBackoff (Duration .ofSeconds (initialRetryDuration ), Duration .ofSeconds (maxRetryDuration ))
97- .withMaxRetries (maxRetryCount )
98- .onRetry (event -> logger .debug ("Retry attempt {} due to {}" , event .getAttemptCount (), event .getLastException ().
85+ .withBackoff (Duration .ofSeconds (DEFAULT_INITIAL_RETRY_DURATION_SECONDS ),
86+ Duration .ofSeconds (DEFAULT_MAX_RETRY_DURATION_SECONDS ))
87+ .withMaxRetries (DEFAULT_MAX_RETRY_COUNT )
88+ .onRetry (event -> LOG .debug ("Retry attempt {} due to {}" , event .getAttemptCount (), event .getLastException ().
9989 getMessage ()))
100- .onSuccess (event -> logger .debug ("Access Token Fetched Successfully." ))
101- .onRetriesExceeded (event -> logger .error ("Retry limit reached for Service account." ))
90+ .onSuccess (event -> LOG .debug ("Access Token Fetched Successfully." ))
91+ .onRetriesExceeded (
92+ event -> LOG .error ("Unable to get service account access token after {} retries." , event .getAttemptCount () - 1 ))
10293 .build ();
10394 }
10495
10596 private boolean isServerError (IOException e ) {
10697 String msg = e .getMessage ();
107- return msg != null && msg .matches ("^5 \\ d{2}$" ); // crude check for 5xx codes
98+ return msg != null && SERVER_ERROR_PATTERN . matcher ( msg ) .matches ();
10899 }
109100
110- private com .google .auth .oauth2 .AccessToken safeGetAccessToken () throws IOException {
101+ private com .google .auth .oauth2 .AccessToken retrieveAccessToken () throws IOException {
111102 try {
112103 return getCredentials ().getAccessToken ();
113104 } catch (IOException e ) {
114105 if (isServerError (e )) {
115106 throw new ServerErrorException (HttpStatus .SC_SERVICE_UNAVAILABLE , "Server error while fetching access token: "
116- + e .getMessage ());
107+ + e .getMessage (), e );
117108 }
118109 throw e ;
119110 }
@@ -126,11 +117,11 @@ public void refresh() throws IOException {
126117 } catch (IOException e ) {
127118 if (isServerError (e )) {
128119 throw new ServerErrorException (HttpStatus .SC_SERVICE_UNAVAILABLE , "Server error during refresh: " +
129- e .getMessage ());
120+ e .getMessage (), e );
130121 }
131- throw ErrorUtils . getProgramFailureException ( new ErrorCategory ( ErrorCategoryEnum . PLUGIN ),
132- "Unable to refresh service account access token." , e . getMessage () ,
133- ErrorType . UNKNOWN , true , e );
122+ throw GCPErrorDetailsProviderUtil . getHttpResponseExceptionDetailsFromChain (
123+ e , "Unable to refresh service account access token." , ErrorType . UNKNOWN , true ,
124+ GCPUtils . GCE_METADATA_SERVER_ERROR_SUPPORTED_DOC_URL );
134125 }
135126 }
136127
0 commit comments