Empty rusoto_s3::GetObjectOutput.body, but works on coworker with same credentials

Hello everyone.
We at Qovery are using rust for some parts of our solution, and I'm having an issue with working code using rusoto_s3.

Issue presentation

This is a minimal reproducible example, working for my colleagues, with the same credentials:

use std::error::Error;
use std::future::Future;
use std::io::Read;

use rusoto_core::{Client, HttpClient, Region}; // v0.44.0
use rusoto_credential::StaticProvider; // v0.44.0
use rusoto_s3::{GetObjectRequest, S3Client, S3}; // v0.44.0

use std::env::var;
use tokio::runtime::Runtime;

fn async_run<F: Future>(future: F) -> F::Output {
    let mut runtime = Runtime::new().expect("unable to create a tokio runtime");
    runtime.block_on(future)
}

fn get_object(
    access_key_id: &str,
    secret_access_key: &str,
    region: Region,
    bucket_name: &str,
    object_key: &str,
) -> Result<String, Box<dyn Error>> {
    let mut s = String::new(); // return value

    let s3_client = S3Client::new_with_client(
        Client::new_with(
            StaticProvider::new(
                access_key_id.to_string(),
                secret_access_key.to_string(),
                None,
                None,
            ),
            HttpClient::new().expect("Could not instantiate a new http client??"),
        ),
        region.clone(),
    );

    let mut or = GetObjectRequest::default();
    or.bucket.push_str(bucket_name);
    or.key.push_str(object_key);
    let oo = s3_client.get_object(or);
    let soo = async_run(oo)?;

    if let Some(body) // once told me i ain't the sharpest tool in the shed…
        = soo.body {
        body.into_blocking_read().read_to_string(&mut s)?;
    }
    else {
        panic!("We don't have a body! How are we to experience the world around us?!");
    }

    Ok(s)
}

fn main() -> Result<(), Box<dyn Error>> {
    let access_key_id =
        var("AWS_ACCESS_KEY_ID").expect("Please specify AWS_ACCESS_KEY_ID in the env");
    let secret_access_key =
        var("AWS_SECRET_ACCESS_KEY").expect("Please specify AWS_SECRET_ACCESS_KEY in the env");

    let data = get_object(
        &access_key_id,
        &secret_access_key,
        Region::UsEast2,
        "kubeconfigs-adwopakdpo221",
        "my-eks-on-us-east-2.yaml",
    );
    match data {
        Ok(s) => {
            assert_eq!(s.is_empty(), false); // panic!
            println!("got data: {}", s);
            Ok(())
        }
        Err(e) => {
            println!("Got an error: {}", e);
            Err(e)
        }
    }
}

Each time, i'm having a panic on assert_eq!(s.is_empty(), false);, meaning we are getting an empty body on our rusoto_s3::GetObjectOutput.

This very same code works with two of my colleagues, with whom i share the credentials (AWS_*), as they get the file content as stdout. We can all request the file via the aws s3 CLI, but for some reason, I alone can't run the code presented. I've checked & triple checked the credentials, they are not the issue.

Running this code with cargo run produces the panic on assert_eq, as well as running it in CLion (still with correct credentials).
HOWEVER: running in debug mode in CLion (with the same run configuration) produces a different behaviour: I'm getting an error (handled in the match data block), which produces the following output:

Got an error: Request ID: None Body: <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>

Questions

  1. Why do i have an empty body and no errors?
  2. Could you please advise on what could i be doing wrong regarding async -> sync?

Closing words

I realize it is more a library usage question rather than a generic rust question, which is why i've created an issue on their github.
If you read this far, i thank you. Please forgive the possible english errors.

Last time I saw an error about this, the answer was to not drop the Runtime until after you are done with the request, and to completely avoid into_blocking_read.

use tokio::io::AsyncReadExt;
fn get_object(
    access_key_id: &str,
    secret_access_key: &str,
    region: Region,
    bucket_name: &str,
    object_key: &str,
) -> Result<String, Box<dyn Error>> {
    let mut s = String::new(); // return value

    async_run(async {
        let s3_client = S3Client::new_with_client(
            Client::new_with(
                StaticProvider::new(
                    access_key_id.to_string(),
                    secret_access_key.to_string(),
                    None,
                    None,
                ),
                HttpClient::new().expect("Could not instantiate a new http client??"),
            ),
            region.clone(),
        );
    
        let mut or = GetObjectRequest::default();
        or.bucket.push_str(bucket_name);
        or.key.push_str(object_key);
        
        match s3_client.get_object(or).await {
            Some(body) => {
                body.into_async_read()
                    .read_to_string(&mut s)
                    .await?;
            }
            None => panic!("We don't have a body! How are we to experience the world around us?!"),
        }
        Ok(())
    })?;

    Ok(s)
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.