Skip to content

Commit 476b25d

Browse files
committed
feat(datadog-tracer-flare): send and zip_and_send methods of tracerflaremanager
1 parent ffd582e commit 476b25d

File tree

1 file changed

+180
-176
lines changed
  • datadog-tracer-flare/src

1 file changed

+180
-176
lines changed

datadog-tracer-flare/src/zip.rs

Lines changed: 180 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -204,188 +204,192 @@ fn generate_payload(
204204
Ok(payload)
205205
}
206206

207-
/// Sends a zip file to the agent via a POST request.
208-
///
209-
/// This function reads the entire zip file into memory, constructs an HTTP request
210-
/// to the agent's flare endpoint, and sends it with retry logic. The agent URL is
211-
/// automatically extended with the `/tracer_flare/v1` path.
212-
///
213-
/// # Arguments
214-
///
215-
/// * `zip` - A file handle to the zip archive to be sent
216-
/// * `log_level` - Log level of the tracer
217-
/// * `agent_task` - Agent
218-
/// * `tracer_flare` - TracerFlareManager instance containing the agent configuration
219-
///
220-
/// # Returns
221-
///
222-
/// * `Ok(())` - If the flare was successfully sent to the agent
223-
/// * `Err(FlareError)` - If any step of the process fails (file reading, network, etc.)
224-
///
225-
/// # Errors
226-
///
227-
/// This function will return an error if:
228-
/// - The zip file cannot be read into memory
229-
/// - The agent URL is invalid
230-
/// - The HTTP request fails after retries
231-
/// - The agent returns a non-success HTTP status code
232-
async fn send(
233-
zip: File,
234-
log_level: LogLevel,
235-
agent_task: AgentTaskFile,
236-
tracer_flare: &TracerFlareManager,
237-
) -> Result<(), FlareError> {
238-
let payload = generate_payload(
239-
zip,
240-
&tracer_flare.language,
241-
&log_level,
242-
&agent_task.args.case_id,
243-
&agent_task.args.hostname,
244-
&agent_task.args.user_handle,
245-
&agent_task.uuid,
246-
)?;
247-
248-
let agent_url = tracer_flare.agent_url.clone() + "/tracer_flare/v1";
249-
let agent_url = match hyper::Uri::from_str(&agent_url) {
250-
Ok(uri) => uri,
251-
Err(_) => {
252-
return Err(FlareError::SendError(format!(
253-
"Invalid agent url: {agent_url}"
254-
)));
255-
}
256-
};
257-
258-
let target = Endpoint {
259-
url: agent_url,
260-
..Default::default()
261-
};
262-
263-
let headers = HashMap::from([(
264-
hyper::header::CONTENT_TYPE.as_str(),
265-
format!("multipart/form-data; boundary={BOUNDARY}"),
266-
)]);
267-
268-
let payload = Bytes::from(payload);
269-
let mut req = target
270-
.to_request_builder(concat!("Tracer/", env!("CARGO_PKG_VERSION")))
271-
.map_err(|_| FlareError::SendError("Unable to create the request".to_owned()))?
272-
.method(Method::POST);
273-
for (key, value) in headers {
274-
req = req.header(key, value);
275-
}
276-
let req = req
277-
.body(hyper_migration::Body::from_bytes(payload))
278-
.map_err(|_| FlareError::SendError("Unable to had the body to the request".to_owned()))?;
279-
280-
let req = hyper_migration::new_default_client().request(req);
281-
282-
match tokio::time::timeout(Duration::from_millis(target.timeout_ms), req).await {
283-
Ok(resp) => match resp {
284-
Ok(body) => {
285-
let response = hyper_migration::into_response(body);
286-
let status = response.status();
287-
if status.is_success() {
288-
Ok(())
289-
} else {
290-
Err(FlareError::SendError(format!(
291-
"Agent returned non-success status for flare send: HTTP {status}"
292-
)))
293-
}
207+
impl TracerFlareManager {
208+
/// Creates a zip archive containing the specified files and directories, obfuscates sensitive
209+
/// data, and sends the flare to the agent.
210+
///
211+
/// # Arguments
212+
///
213+
/// * `files` - A vector of strings representing the paths of files and directories to include
214+
/// in the zip archive.
215+
/// * `log_level` - Log level of the tracer.
216+
/// * `tracer_flare` - TracerFlareManager instance containing the agent configuration and task
217+
/// data.
218+
/// * `send_action` - ReturnAction to perform by the tracer flare. Must be a Send action or the
219+
/// function will return an Error.
220+
///
221+
/// # Returns
222+
///
223+
/// * `Ok(())` - If the zip archive was created, obfuscated, and sent successfully.
224+
/// * `Err(FlareError)` - An error if any step of the process fails.
225+
///
226+
/// # Errors
227+
///
228+
/// This function will return an error if:
229+
/// - Any problem happened while zipping the file.
230+
/// - The obfuscation process fails.
231+
/// - The zip file cannot be sent to the agent.
232+
/// - No agent task was received by the tracer_flare.
233+
///
234+
/// # Examples
235+
///
236+
/// ```rust no_run
237+
/// use datadog_tracer_flare::{TracerFlareManager, ReturnAction};
238+
/// use datadog_remote_config::config::agent_task::{AgentTaskFile, AgentTask};
239+
/// use std::num::NonZeroU64;
240+
///
241+
/// #[tokio::main]
242+
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
243+
/// let tracer_flare = TracerFlareManager::default();
244+
///
245+
/// // ... listen to remote config and receive an agent task ...
246+
///
247+
/// // Simulate receiving a Send action from remote config
248+
/// let task = AgentTaskFile {
249+
/// args: AgentTask {
250+
/// case_id: NonZeroU64::new(123).unwrap(),
251+
/// hostname: "test-host".to_string(),
252+
/// user_handle: "[email protected]".to_string(),
253+
/// },
254+
/// task_type: "tracer_flare".to_string(),
255+
/// uuid: "test-uuid".to_string(),
256+
/// };
257+
/// let send_action = ReturnAction::Send(task);
258+
///
259+
/// let files = vec![
260+
/// "/path/to/logs".to_string(),
261+
/// "/path/to/config.txt".to_string(),
262+
/// ];
263+
///
264+
/// match tracer_flare.zip_and_send(files, send_action).await {
265+
/// Ok(_) => println!("Flare sent successfully"),
266+
/// Err(e) => eprintln!("Failed to send flare: {}", e),
267+
/// }
268+
/// Ok(())
269+
/// }
270+
/// ```
271+
pub async fn zip_and_send(
272+
&self,
273+
files: Vec<String>,
274+
send_action: ReturnAction,
275+
) -> Result<(), FlareError> {
276+
let agent_task = match send_action {
277+
ReturnAction::Send(agent_task) => agent_task,
278+
_ => {
279+
return Err(FlareError::SendError(
280+
"Trying to send the flare with a non Send Action".to_string(),
281+
))
294282
}
295-
Err(e) => Err(FlareError::SendError(format!("Network error: {e}"))),
296-
},
297-
Err(_) => Err(FlareError::SendError("Api timeout exhausted".to_owned())),
298-
}
299-
}
283+
};
300284

301-
/// Creates a zip archive containing the specified files and directories, obfuscates sensitive data,
302-
/// and sends the flare to the agent.
303-
///
304-
/// # Arguments
305-
///
306-
/// * `files` - A vector of strings representing the paths of files and directories to include in
307-
/// the zip archive.
308-
/// * `log_level` - Log level of the tracer.
309-
/// * `tracer_flare` - TracerFlareManager instance containing the agent configuration and task data.
310-
/// * `send_action` - ReturnAction to perform by the tracer flare. Must be a Send action or the
311-
/// function will return an Error.
312-
///
313-
/// # Returns
314-
///
315-
/// * `Ok(())` - If the zip archive was created, obfuscated, and sent successfully.
316-
/// * `Err(FlareError)` - An error if any step of the process fails.
317-
///
318-
/// # Errors
319-
///
320-
/// This function will return an error if:
321-
/// - Any problem happened while zipping the file.
322-
/// - The obfuscation process fails.
323-
/// - The zip file cannot be sent to the agent.
324-
/// - No agent task was received by the tracer_flare.
325-
///
326-
/// # Examples
327-
///
328-
/// ```rust no_run
329-
/// use datadog_tracer_flare::zip::zip_and_send;
330-
/// use datadog_tracer_flare::{TracerFlareManager, ReturnAction};
331-
/// use datadog_remote_config::config::agent_task::{AgentTaskFile, AgentTask};
332-
/// use std::num::NonZeroU64;
333-
///
334-
/// #[tokio::main]
335-
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
336-
/// let tracer_flare = TracerFlareManager::default();
337-
///
338-
/// // ... listen to remote config and receive an agent task ...
339-
///
340-
/// // Simulate receiving a Send action from remote config
341-
/// let task = AgentTaskFile {
342-
/// args: AgentTask {
343-
/// case_id: NonZeroU64::new(123).unwrap(),
344-
/// hostname: "test-host".to_string(),
345-
/// user_handle: "[email protected]".to_string(),
346-
/// },
347-
/// task_type: "tracer_flare".to_string(),
348-
/// uuid: "test-uuid".to_string(),
349-
/// };
350-
/// let send_action = ReturnAction::Send(task);
351-
///
352-
/// let files = vec![
353-
/// "/path/to/logs".to_string(),
354-
/// "/path/to/config.txt".to_string(),
355-
/// ];
356-
///
357-
/// match zip_and_send(files, &tracer_flare, send_action).await {
358-
/// Ok(_) => println!("Flare sent successfully"),
359-
/// Err(e) => eprintln!("Failed to send flare: {}", e),
360-
/// }
361-
/// Ok(())
362-
/// }
363-
/// ```
364-
pub async fn zip_and_send(
365-
files: Vec<String>,
366-
tracer_flare: &TracerFlareManager,
367-
send_action: ReturnAction,
368-
) -> Result<(), FlareError> {
369-
let agent_task = match send_action {
370-
ReturnAction::Send(agent_task) => agent_task,
371-
_ => {
372-
return Err(FlareError::SendError(
373-
"Trying to send the flare with a non Send Action".to_string(),
374-
))
375-
}
376-
};
285+
let zip = zip_files(files)?;
377286

378-
let zip = zip_files(files)?;
287+
// APMSP-2118 - TODO: Implement obfuscation of sensitive data
379288

380-
// APMSP-2118 - TODO: Implement obfuscation of sensitive data
289+
let log_level = self
290+
.current_log_level
291+
.lock_or_panic()
292+
// Default log level
293+
.unwrap_or(LogLevel::Debug);
381294

382-
let log_level = tracer_flare
383-
.current_log_level
384-
.lock_or_panic()
385-
// Default log level
386-
.unwrap_or(LogLevel::Debug);
295+
self.send(zip, log_level, agent_task).await
296+
}
387297

388-
send(zip, log_level, agent_task, tracer_flare).await
298+
/// Sends a zip file to the agent via a POST request.
299+
///
300+
/// This function reads the entire zip file into memory, constructs an HTTP request
301+
/// to the agent's flare endpoint, and sends it with retry logic. The agent URL is
302+
/// automatically extended with the `/tracer_flare/v1` path.
303+
///
304+
/// # Arguments
305+
///
306+
/// * `zip` - A file handle to the zip archive to be sent
307+
/// * `log_level` - Log level of the tracer
308+
/// * `agent_task` - Agent
309+
/// * `tracer_flare` - TracerFlareManager instance containing the agent configuration
310+
///
311+
/// # Returns
312+
///
313+
/// * `Ok(())` - If the flare was successfully sent to the agent
314+
/// * `Err(FlareError)` - If any step of the process fails (file reading, network, etc.)
315+
///
316+
/// # Errors
317+
///
318+
/// This function will return an error if:
319+
/// - The zip file cannot be read into memory
320+
/// - The agent URL is invalid
321+
/// - The HTTP request fails after retries
322+
/// - The agent returns a non-success HTTP status code
323+
async fn send(
324+
&self,
325+
zip: File,
326+
log_level: LogLevel,
327+
agent_task: AgentTaskFile,
328+
) -> Result<(), FlareError> {
329+
let payload = generate_payload(
330+
zip,
331+
&self.language,
332+
&log_level,
333+
&agent_task.args.case_id,
334+
&agent_task.args.hostname,
335+
&agent_task.args.user_handle,
336+
&agent_task.uuid,
337+
)?;
338+
339+
let agent_url = self.agent_url.clone() + "/tracer_flare/v1";
340+
let agent_url = match hyper::Uri::from_str(&agent_url) {
341+
Ok(uri) => uri,
342+
Err(_) => {
343+
return Err(FlareError::SendError(format!(
344+
"Invalid agent url: {agent_url}"
345+
)));
346+
}
347+
};
348+
349+
let target = Endpoint {
350+
url: agent_url,
351+
..Default::default()
352+
};
353+
354+
let headers = HashMap::from([(
355+
hyper::header::CONTENT_TYPE.as_str(),
356+
format!("multipart/form-data; boundary={BOUNDARY}"),
357+
)]);
358+
359+
let payload = Bytes::from(payload);
360+
let mut req = target
361+
.to_request_builder(concat!("Tracer/", env!("CARGO_PKG_VERSION")))
362+
.map_err(|_| FlareError::SendError("Unable to create the request".to_owned()))?
363+
.method(Method::POST);
364+
for (key, value) in headers {
365+
req = req.header(key, value);
366+
}
367+
let req = req
368+
.body(hyper_migration::Body::from_bytes(payload))
369+
.map_err(|_| {
370+
FlareError::SendError("Unable to had the body to the request".to_owned())
371+
})?;
372+
373+
let req = hyper_migration::new_default_client().request(req);
374+
375+
match tokio::time::timeout(Duration::from_millis(target.timeout_ms), req).await {
376+
Ok(resp) => match resp {
377+
Ok(body) => {
378+
let response = hyper_migration::into_response(body);
379+
let status = response.status();
380+
if status.is_success() {
381+
Ok(())
382+
} else {
383+
Err(FlareError::SendError(format!(
384+
"Agent returned non-success status for flare send: HTTP {status}"
385+
)))
386+
}
387+
}
388+
Err(e) => Err(FlareError::SendError(format!("Network error: {e}"))),
389+
},
390+
Err(_) => Err(FlareError::SendError("Api timeout exhausted".to_owned())),
391+
}
392+
}
389393
}
390394

391395
#[cfg(test)]

0 commit comments

Comments
 (0)