@@ -8,6 +8,7 @@ use crate::{mk_canary, CanaryEnv};
88use anyhow:: Context ;
99use aws_config:: SdkConfig ;
1010use aws_sdk_s3 as s3;
11+ use s3:: config:: Region ;
1112use s3:: presigning:: PresigningConfig ;
1213use s3:: primitives:: ByteStream ;
1314use std:: time:: Duration ;
@@ -17,10 +18,15 @@ const METADATA_TEST_VALUE: &str = "some value";
1718
1819mk_canary ! ( "s3" , |sdk_config: & SdkConfig , env: & CanaryEnv | s3_canary(
1920 s3:: Client :: new( sdk_config) ,
20- env. s3_bucket_name. clone( )
21+ env. s3_bucket_name. clone( ) ,
22+ env. s3_mrap_bucket_arn. clone( )
2123) ) ;
2224
23- pub async fn s3_canary ( client : s3:: Client , s3_bucket_name : String ) -> anyhow:: Result < ( ) > {
25+ pub async fn s3_canary (
26+ client : s3:: Client ,
27+ s3_bucket_name : String ,
28+ s3_mrap_bucket_arn : String ,
29+ ) -> anyhow:: Result < ( ) > {
2430 let test_key = Uuid :: new_v4 ( ) . as_u128 ( ) . to_string ( ) ;
2531
2632 // Look for the test object and expect that it doesn't exist
@@ -82,39 +88,104 @@ pub async fn s3_canary(client: s3::Client, s3_bucket_name: String) -> anyhow::Re
8288 return Err ( CanaryError ( format ! ( "presigned URL returned bad data: {:?}" , response) ) . into ( ) ) ;
8389 }
8490
85- let mut result = Ok ( ( ) ) ;
86- match output. metadata ( ) {
87- Some ( map) => {
88- // Option::as_deref doesn't work here since the deref of &String is String
89- let value = map. get ( "something" ) . map ( |s| s. as_str ( ) ) . unwrap_or ( "" ) ;
90- if value != METADATA_TEST_VALUE {
91- result = Err ( CanaryError ( format ! (
91+ let metadata_value = output
92+ . metadata ( )
93+ . and_then ( |m| m. get ( "something" ) )
94+ . map ( String :: as_str) ;
95+ let result: anyhow:: Result < ( ) > = match metadata_value {
96+ Some ( value) => {
97+ if value == METADATA_TEST_VALUE {
98+ let payload = output
99+ . body
100+ . collect ( )
101+ . await
102+ . context ( "download s3::GetObject[2] body" ) ?
103+ . into_bytes ( ) ;
104+ if std:: str:: from_utf8 ( payload. as_ref ( ) ) . context ( "s3 payload" ) ? == "test" {
105+ Ok ( ( ) )
106+ } else {
107+ Err ( CanaryError ( "S3 object body didn't match what was put there" . into ( ) ) . into ( ) )
108+ }
109+ } else {
110+ Err ( CanaryError ( format ! (
92111 "S3 metadata was incorrect. Expected `{}` but got `{}`." ,
93112 METADATA_TEST_VALUE , value
94113 ) )
95- . into ( ) ) ;
114+ . into ( ) )
96115 }
97116 }
98- None => {
99- result = Err ( CanaryError ( "S3 metadata was missing" . into ( ) ) . into ( ) ) ;
100- }
101- }
117+ None => Err ( CanaryError ( "S3 metadata was missing" . into ( ) ) . into ( ) ) ,
118+ } ;
102119
103- let payload = output
104- . body
105- . collect ( )
120+ // Delete the test object
121+ client
122+ . delete_object ( )
123+ . bucket ( & s3_bucket_name)
124+ . key ( & test_key)
125+ . send ( )
106126 . await
107- . context ( "download s3::GetObject[2] body" ) ?
108- . into_bytes ( ) ;
109- if std:: str:: from_utf8 ( payload. as_ref ( ) ) . context ( "s3 payload" ) ? != "test" {
110- result = Err ( CanaryError ( "S3 object body didn't match what was put there" . into ( ) ) . into ( ) ) ;
111- }
127+ . context ( "s3::DeleteObject" ) ?;
128+
129+ // Return early if the result is an error
130+ result?;
131+
132+ // We deliberately use a region that doesn't exist here so that we can
133+ // ensure these requests are SigV4a requests. Because the current endpoint
134+ // resolver always resolves the wildcard region ('*') for SigV4a requests,
135+ // setting a fictitious region ensures that the request would fail if it was
136+ // a SigV4 request. Therefore, because the request doesn't fail, we can be
137+ // sure it's a successful Sigv4a request.
138+ let config_override = s3:: Config :: builder ( ) . region ( Region :: new ( "parts-unknown" ) ) ;
139+ // Put the test object
140+ client
141+ . put_object ( )
142+ . bucket ( & s3_mrap_bucket_arn)
143+ . key ( & test_key)
144+ . body ( ByteStream :: from_static ( b"test" ) )
145+ . metadata ( "something" , METADATA_TEST_VALUE )
146+ . customize ( )
147+ . config_override ( config_override. clone ( ) )
148+ . send ( )
149+ . await
150+ . context ( "s3::PutObject[MRAP]" ) ?;
151+
152+ // Get the test object and verify it looks correct
153+ let output = client
154+ . get_object ( )
155+ . bucket ( & s3_mrap_bucket_arn)
156+ . key ( & test_key)
157+ . customize ( )
158+ . config_override ( config_override. clone ( ) )
159+ . send ( )
160+ . await
161+ . context ( "s3::GetObject[MRAP]" ) ?;
162+
163+ let metadata_value = output
164+ . metadata ( )
165+ . and_then ( |m| m. get ( "something" ) )
166+ . map ( String :: as_str) ;
167+ let result = match metadata_value {
168+ Some ( value) => {
169+ if value == METADATA_TEST_VALUE {
170+ Ok ( ( ) )
171+ } else {
172+ Err ( CanaryError ( format ! (
173+ "S3 metadata was incorrect. Expected `{}` but got `{}`." ,
174+ METADATA_TEST_VALUE , value
175+ ) )
176+ . into ( ) )
177+ }
178+ }
179+ None => Err ( CanaryError ( "S3 metadata was missing" . into ( ) ) . into ( ) ) ,
180+ } ;
112181
113182 // Delete the test object
114183 client
115184 . delete_object ( )
116- . bucket ( & s3_bucket_name )
185+ . bucket ( & s3_mrap_bucket_arn )
117186 . key ( & test_key)
187+ . customize ( )
188+ . config_override ( config_override)
118189 . send ( )
119190 . await
120191 . context ( "s3::DeleteObject" ) ?;
@@ -123,8 +194,12 @@ pub async fn s3_canary(client: s3::Client, s3_bucket_name: String) -> anyhow::Re
123194}
124195
125196// This test runs against an actual AWS account. Comment out the `ignore` to run it.
126- // Be sure to set the `TEST_S3_BUCKET` environment variable to the S3 bucket to use,
127- // and also make sure the credential profile sets the region (or set `AWS_DEFAULT_PROFILE`).
197+ // Be sure the following environment variables are set:
198+ //
199+ // - `TEST_S3_BUCKET`: The S3 bucket to use
200+ // - `TEST_S3_MRAP_BUCKET_ARN`: The MRAP bucket ARN to use
201+ //
202+ // Also, make sure the correct region (likely `us-west-2`) by the credentials or explictly.
128203#[ ignore]
129204#[ cfg( test) ]
130205#[ tokio:: test]
@@ -134,6 +209,7 @@ async fn test_s3_canary() {
134209 s3_canary (
135210 client,
136211 std:: env:: var ( "TEST_S3_BUCKET" ) . expect ( "TEST_S3_BUCKET must be set" ) ,
212+ std:: env:: var ( "TEST_S3_MRAP_BUCKET_ARN" ) . expect ( "TEST_S3_MRAP_BUCKET_ARN must be set" ) ,
137213 )
138214 . await
139215 . expect ( "success" ) ;
0 commit comments