Skip to content

Commit 0047b27

Browse files
fix: telemetry credential provider for cognito (#131)
1 parent 8553880 commit 0047b27

File tree

4 files changed

+83
-38
lines changed

4 files changed

+83
-38
lines changed

crates/chat-cli/src/api_client/clients/client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ impl Client {
8181
) -> Result<(), ApiClientError> {
8282
match &self.inner {
8383
inner::Inner::Codewhisperer(client) => {
84-
let _ = client
84+
client
8585
.send_telemetry_event()
8686
.telemetry_event(telemetry_event)
8787
.user_context(user_context)
@@ -91,7 +91,7 @@ impl Client {
9191
})
9292
.set_profile_arn(self.profile.as_ref().map(|p| p.arn.clone()))
9393
.send()
94-
.await;
94+
.await?;
9595
Ok(())
9696
},
9797
inner::Inner::Mock => Ok(()),

crates/chat-cli/src/api_client/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use amzn_codewhisperer_client::operation::generate_completions::GenerateCompletionsError;
22
use amzn_codewhisperer_client::operation::list_available_customizations::ListAvailableCustomizationsError;
33
use amzn_codewhisperer_client::operation::list_available_profiles::ListAvailableProfilesError;
4+
use amzn_codewhisperer_client::operation::send_telemetry_event::SendTelemetryEventError;
45
pub use amzn_codewhisperer_streaming_client::operation::generate_assistant_response::GenerateAssistantResponseError;
56
use amzn_codewhisperer_streaming_client::types::error::ChatResponseStreamError as CodewhispererChatResponseStreamError;
67
use amzn_consolas_client::operation::generate_recommendations::GenerateRecommendationsError;
@@ -29,6 +30,10 @@ pub enum ApiClientError {
2930
#[error("{}", SdkErrorDisplay(.0))]
3031
ListAvailableServices(#[from] SdkError<ListCustomizationsError, HttpResponse>),
3132

33+
// Telemetry client error
34+
#[error("{}", SdkErrorDisplay(.0))]
35+
SendTelemetryEvent(#[from] SdkError<SendTelemetryEventError, HttpResponse>),
36+
3237
// Send message errors
3338
#[error("{}", SdkErrorDisplay(.0))]
3439
CodewhispererGenerateAssistantResponse(#[from] SdkError<GenerateAssistantResponseError, HttpResponse>),

crates/chat-cli/src/telemetry/cognito.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::time::SystemTime;
2+
13
use amzn_toolkit_telemetry_client::config::BehaviorVersion;
24
use aws_credential_types::provider::error::CredentialsError;
35
use aws_credential_types::{
@@ -8,6 +10,10 @@ use aws_sdk_cognitoidentity::primitives::{
810
DateTime,
911
DateTimeFormat,
1012
};
13+
use tracing::{
14+
trace,
15+
warn,
16+
};
1117

1218
use crate::aws_common::app_name;
1319
use crate::database::{
@@ -20,6 +26,8 @@ pub async fn get_cognito_credentials_send(
2026
database: &mut Database,
2127
telemetry_stage: &TelemetryStage,
2228
) -> Result<Credentials, CredentialsError> {
29+
trace!("Creating new cognito credentials");
30+
2331
let conf = aws_sdk_cognitoidentity::Config::builder()
2432
.behavior_version(BehaviorVersion::v2025_01_17())
2533
.region(telemetry_stage.region.clone())
@@ -80,6 +88,10 @@ pub async fn get_cognito_credentials(
8088
session_token,
8189
expiration,
8290
}) => {
91+
if is_expired(expiration.as_ref()) {
92+
return get_cognito_credentials_send(database, telemetry_stage).await;
93+
}
94+
8395
let Some(access_key_id) = access_key_id else {
8496
return get_cognito_credentials_send(database, telemetry_stage).await;
8597
};
@@ -104,12 +116,12 @@ pub async fn get_cognito_credentials(
104116

105117
#[derive(Debug)]
106118
pub struct CognitoProvider {
107-
credentials: Credentials,
119+
telemetry_stage: TelemetryStage,
108120
}
109121

110122
impl CognitoProvider {
111-
pub fn new(credentials: Credentials) -> CognitoProvider {
112-
CognitoProvider { credentials }
123+
pub fn new(telemetry_stage: TelemetryStage) -> CognitoProvider {
124+
CognitoProvider { telemetry_stage }
113125
}
114126
}
115127

@@ -118,7 +130,36 @@ impl provider::ProvideCredentials for CognitoProvider {
118130
where
119131
Self: 'a,
120132
{
121-
provider::future::ProvideCredentials::new(async { Ok(self.credentials.clone()) })
133+
provider::future::ProvideCredentials::new(async {
134+
match Database::new().await {
135+
Ok(mut db) => get_cognito_credentials(&mut db, &self.telemetry_stage).await,
136+
Err(err) => Err(CredentialsError::provider_error(format!(
137+
"failed to get database: {:?}",
138+
err
139+
))),
140+
}
141+
})
142+
}
143+
}
144+
145+
fn is_expired(expiration: Option<&String>) -> bool {
146+
let expiration = if let Some(v) = expiration {
147+
v
148+
} else {
149+
warn!("no cognito expiration was saved");
150+
return true;
151+
};
152+
153+
match DateTime::from_str(expiration, DateTimeFormat::DateTime) {
154+
Ok(expiration) => {
155+
// Check if the expiration is at least after five minutes after the current time.
156+
let curr: DateTime = (SystemTime::now() + std::time::Duration::from_secs(60 * 5)).into();
157+
expiration < curr
158+
},
159+
Err(err) => {
160+
warn!(?err, "invalid cognito expiration was saved");
161+
true
162+
},
122163
}
123164
}
124165

crates/chat-cli/src/telemetry/mod.rs

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ use amzn_toolkit_telemetry_client::{
2525
Config,
2626
};
2727
use aws_credential_types::provider::SharedCredentialsProvider;
28-
use cognito::{
29-
CognitoProvider,
30-
get_cognito_credentials,
31-
};
28+
use cognito::CognitoProvider;
3229
use endpoint::StaticEndpoint;
3330
pub use install_method::{
3431
InstallMethod,
@@ -149,7 +146,7 @@ impl TelemetryThread {
149146
let (tx, mut rx) = mpsc::unbounded_channel();
150147
let handle = tokio::spawn(async move {
151148
while let Some(event) = rx.recv().await {
152-
trace!("Sending telemetry event: {:?}", event);
149+
trace!("TelemetryThread received new telemetry event: {:?}", event);
153150
telemetry_client.send_event(event).await;
154151
}
155152
});
@@ -319,24 +316,21 @@ impl TelemetryClient {
319316
&& database.settings.get_bool(Setting::TelemetryEnabled).unwrap_or(true);
320317

321318
// If telemetry is disabled we do not emit using toolkit_telemetry
322-
let toolkit_telemetry_client = match telemetry_enabled {
323-
true => match get_cognito_credentials(database, &TelemetryStage::EXTERNAL_PROD).await {
324-
Ok(credentials) => Some(ToolkitTelemetryClient::from_conf(
325-
Config::builder()
326-
.http_client(crate::aws_common::http_client::client())
327-
.behavior_version(BehaviorVersion::v2025_01_17())
328-
.endpoint_resolver(StaticEndpoint(TelemetryStage::EXTERNAL_PROD.endpoint))
329-
.app_name(app_name())
330-
.region(TelemetryStage::EXTERNAL_PROD.region.clone())
331-
.credentials_provider(SharedCredentialsProvider::new(CognitoProvider::new(credentials)))
332-
.build(),
333-
)),
334-
Err(err) => {
335-
error!("Failed to acquire cognito credentials: {err}");
336-
None
337-
},
338-
},
339-
false => None,
319+
let toolkit_telemetry_client = if telemetry_enabled {
320+
Some(ToolkitTelemetryClient::from_conf(
321+
Config::builder()
322+
.http_client(crate::aws_common::http_client::client())
323+
.behavior_version(BehaviorVersion::v2025_01_17())
324+
.endpoint_resolver(StaticEndpoint(TelemetryStage::EXTERNAL_PROD.endpoint))
325+
.app_name(app_name())
326+
.region(TelemetryStage::EXTERNAL_PROD.region.clone())
327+
.credentials_provider(SharedCredentialsProvider::new(CognitoProvider::new(
328+
TelemetryStage::EXTERNAL_PROD,
329+
)))
330+
.build(),
331+
))
332+
} else {
333+
None
340334
};
341335

342336
fn client_id(env: &Env, database: &mut Database, telemetry_enabled: bool) -> Result<Uuid, TelemetryError> {
@@ -400,38 +394,43 @@ impl TelemetryClient {
400394
{
401395
Ok(event) => event,
402396
Err(err) => {
403-
error!(err =% DisplayErrorContext(err), "Failed to send telemetry event");
397+
error!(err =% DisplayErrorContext(err), "Failed to send cw telemetry event");
404398
return;
405399
},
406400
};
407401

402+
let event = TelemetryEvent::ChatAddMessageEvent(chat_add_message_event);
403+
debug!(
404+
?event,
405+
?user_context,
406+
telemetry_enabled = self.telemetry_enabled,
407+
"Sending cw telemetry event"
408+
);
408409
if let Err(err) = self
409410
.codewhisperer_client
410-
.send_telemetry_event(
411-
TelemetryEvent::ChatAddMessageEvent(chat_add_message_event),
412-
user_context,
413-
self.telemetry_enabled,
414-
)
411+
.send_telemetry_event(event, user_context, self.telemetry_enabled)
415412
.await
416413
{
417-
error!(err =% DisplayErrorContext(err), "Failed to send telemetry event");
414+
error!(err =% DisplayErrorContext(err), "Failed to send cw telemetry event");
418415
}
419416
}
420417
}
421418

422419
async fn send_telemetry_toolkit_metric(&self, event: Event) {
423420
let Some(toolkit_telemetry_client) = self.toolkit_telemetry_client.clone() else {
421+
trace!("not sending toolkit metric - client does not exist");
424422
return;
425423
};
426424
let client_id = self.client_id;
427425
let Some(metric_datum) = event.into_metric_datum() else {
426+
trace!("not sending toolkit metric - metric datum does not exist");
428427
return;
429428
};
430429

431430
let product = AwsProduct::CodewhispererTerminal;
432431
let metric_name = metric_datum.metric_name().to_owned();
433432

434-
debug!(?product, ?metric_datum, "Posting metrics");
433+
debug!(?client_id, ?product, ?metric_datum, "Sending toolkit telemetry event");
435434
if let Err(err) = toolkit_telemetry_client
436435
.post_metrics()
437436
.aws_product(product)
@@ -445,7 +444,7 @@ impl TelemetryClient {
445444
.await
446445
.map_err(DisplayErrorContext)
447446
{
448-
error!(%err, ?metric_name, "Failed to post metric");
447+
error!(%err, ?metric_name, "Failed to post toolkit metric");
449448
}
450449
}
451450

0 commit comments

Comments
 (0)