@@ -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