Skip to content

Commit 7367522

Browse files
authored
Merge pull request #7758 from ClSlaid/handle-session-token-copy-into
feat(query): add security token support for AWS S3
2 parents 4c4a839 + ceae33a commit 7367522

File tree

13 files changed

+124
-7
lines changed

13 files changed

+124
-7
lines changed

docs/doc/21-load-data/01-s3.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ To use the COPY data loading, you will need the following information:
8282
* Your AWS account’s access keys, such as:
8383
* Access Key ID: *your-access-key-id*
8484
* Secret Access Key: *your-secret-access-key*
85-
85+
* Security Token (Optional): *your-aws-temporary-access-token*
8686

8787
### Step 5: Copy Data into the Target Tables
8888

@@ -95,7 +95,7 @@ Using this URI and keys, execute the following statement, replacing the placehol
9595
```sql
9696
COPY INTO books
9797
FROM 's3://databend-bohu/data/'
98-
credentials=(aws_key_id='<your-access-key-id>' aws_secret_key='<your-secret-access-key>')
98+
credentials=(aws_key_id='<your-access-key-id>' aws_secret_key='<your-secret-access-key>' [aws-token='<your-aws-temporary-access-token>'])
9999
pattern ='.*[.]csv'
100100
file_format = (type = 'CSV' field_delimiter = ',' record_delimiter = '\n' skip_header = 0);
101101
```

src/common/storage/src/config.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,13 @@ pub struct StorageS3Config {
200200
pub bucket: String,
201201
pub access_key_id: String,
202202
pub secret_access_key: String,
203+
/// Temporary security token used for authentications
204+
///
205+
/// This recommended to use since users don't need to store their permanent credentials in their
206+
/// scripts or worksheets.
207+
///
208+
/// refer to [documentations](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html) for details.
209+
pub security_token: String,
203210
pub master_key: String,
204211
pub root: String,
205212
/// This flag is used internally to control whether databend load
@@ -220,6 +227,7 @@ impl Default for StorageS3Config {
220227
bucket: "".to_string(),
221228
access_key_id: "".to_string(),
222229
secret_access_key: "".to_string(),
230+
security_token: "".to_string(),
223231
master_key: "".to_string(),
224232
root: "".to_string(),
225233
disable_credential_loader: false,
@@ -242,6 +250,7 @@ impl Debug for StorageS3Config {
242250
"secret_access_key",
243251
&mask_string(&self.secret_access_key, 3),
244252
)
253+
.field("security_token", &mask_string(&self.security_token, 3))
245254
.field("master_key", &mask_string(&self.master_key, 3))
246255
.finish()
247256
}

src/common/storage/src/location.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ pub fn parse_uri_location(l: &UriLocation) -> Result<(StorageParams, String)> {
115115
.or_else(|| l.connection.get("aws_secret_key"))
116116
.cloned()
117117
.unwrap_or_default(),
118+
security_token: l
119+
.connection
120+
.get("security_token")
121+
.or_else(|| l.connection.get("aws_token"))
122+
.cloned()
123+
.unwrap_or_default(),
118124
master_key: l.connection.get("master_key").cloned().unwrap_or_default(),
119125
root: root.to_string(),
120126
disable_credential_loader: true,

src/common/storage/tests/location.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ fn test_parse_uri_location() -> Result<()> {
4848
bucket: "test".to_string(),
4949
access_key_id: "access_key_id".to_string(),
5050
secret_access_key: "secret_access_key".to_string(),
51+
security_token: "".to_string(),
5152
master_key: "".to_string(),
5253
root: "/tmp/".to_string(),
5354
disable_credential_loader: true,
@@ -65,6 +66,7 @@ fn test_parse_uri_location() -> Result<()> {
6566
connection: vec![
6667
("aws_key_id", "access_key_id"),
6768
("aws_secret_key", "secret_access_key"),
69+
("security_token", "security_token"),
6870
]
6971
.iter()
7072
.map(|(k, v)| (k.to_string(), v.to_string()))
@@ -77,6 +79,38 @@ fn test_parse_uri_location() -> Result<()> {
7779
bucket: "test".to_string(),
7880
access_key_id: "access_key_id".to_string(),
7981
secret_access_key: "secret_access_key".to_string(),
82+
security_token: "security_token".to_string(),
83+
master_key: "".to_string(),
84+
root: "/tmp/".to_string(),
85+
disable_credential_loader: true,
86+
enable_virtual_host_style: false,
87+
}),
88+
"/".to_string(),
89+
),
90+
),
91+
(
92+
"s3_with_aws_security_token",
93+
UriLocation {
94+
protocol: "s3".to_string(),
95+
name: "test".to_string(),
96+
path: "/tmp/".to_string(),
97+
connection: vec![
98+
("aws_key_id", "access_key_id"),
99+
("aws_secret_key", "secret_access_key"),
100+
("aws_token", "security_token"),
101+
]
102+
.iter()
103+
.map(|(k, v)| (k.to_string(), v.to_string()))
104+
.collect(),
105+
},
106+
(
107+
StorageParams::S3(StorageS3Config {
108+
endpoint_url: STORAGE_S3_DEFAULT_ENDPOINT.to_string(),
109+
region: "".to_string(),
110+
bucket: "test".to_string(),
111+
access_key_id: "access_key_id".to_string(),
112+
secret_access_key: "secret_access_key".to_string(),
113+
security_token: "security_token".to_string(),
80114
master_key: "".to_string(),
81115
root: "/tmp/".to_string(),
82116
disable_credential_loader: true,

src/meta/proto-conv/src/config_from_to_protobuf_impl.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl FromToProto for StorageS3Config {
3535
endpoint_url: p.endpoint_url,
3636
access_key_id: p.access_key_id,
3737
secret_access_key: p.secret_access_key,
38+
security_token: p.security_token,
3839
bucket: p.bucket,
3940
root: p.root,
4041
master_key: p.master_key,
@@ -51,6 +52,7 @@ impl FromToProto for StorageS3Config {
5152
endpoint_url: self.endpoint_url.clone(),
5253
access_key_id: self.access_key_id.clone(),
5354
secret_access_key: self.secret_access_key.clone(),
55+
security_token: self.security_token.clone(),
5456
bucket: self.bucket.clone(),
5557
root: self.root.clone(),
5658
master_key: self.master_key.clone(),

src/meta/proto-conv/src/util.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ const META_CHANGE_LOG: &[(u64, &str)] = &[
4343
"2022-09-09: Add: table.proto/{TableCopiedFileInfo,TableCopiedFileLock} type",
4444
),
4545
(8, "2022-09-16: Add: users.proto/StageFile::entity_tag"),
46+
(
47+
9,
48+
"2022-09-20: Add: config.proto/S3StorageConfig::security_token",
49+
),
4650
];
4751

4852
pub const VER: u64 = META_CHANGE_LOG.last().unwrap().0;

src/meta/proto-conv/tests/it/user_proto_conv.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ pub(crate) fn test_s3_stage_info() -> mt::UserStageInfo {
102102
root: "/data/files".to_string(),
103103
access_key_id: "my_key_id".to_string(),
104104
secret_access_key: "my_secret_key".to_string(),
105+
security_token: "my_security_token".to_string(),
105106
master_key: "my_master_key".to_string(),
106107
..Default::default()
107108
}),

src/meta/proto-conv/tests/it/user_stage.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,55 @@ fn test_user_stage_gcs_latest() -> anyhow::Result<()> {
4343
Ok(())
4444
}
4545

46+
#[test]
47+
fn test_user_stage_s3_v8() -> anyhow::Result<()> {
48+
// Encoded data of version 8 of user_stage_s3:
49+
// It is generated with common::test_pb_from_to.
50+
let user_stage_s3_v8 = vec![
51+
10, 24, 115, 51, 58, 47, 47, 109, 121, 98, 117, 99, 107, 101, 116, 47, 100, 97, 116, 97,
52+
47, 102, 105, 108, 101, 115, 16, 1, 26, 100, 10, 98, 10, 96, 18, 24, 104, 116, 116, 112,
53+
115, 58, 47, 47, 115, 51, 46, 97, 109, 97, 122, 111, 110, 97, 119, 115, 46, 99, 111, 109,
54+
26, 9, 109, 121, 95, 107, 101, 121, 95, 105, 100, 34, 13, 109, 121, 95, 115, 101, 99, 114,
55+
101, 116, 95, 107, 101, 121, 42, 8, 109, 121, 98, 117, 99, 107, 101, 116, 50, 11, 47, 100,
56+
97, 116, 97, 47, 102, 105, 108, 101, 115, 58, 13, 109, 121, 95, 109, 97, 115, 116, 101,
57+
114, 95, 107, 101, 121, 160, 6, 8, 168, 6, 1, 34, 20, 8, 1, 16, 128, 8, 26, 1, 124, 34, 2,
58+
47, 47, 40, 2, 160, 6, 8, 168, 6, 1, 42, 10, 10, 3, 32, 154, 5, 16, 142, 8, 24, 1, 50, 4,
59+
116, 101, 115, 116, 160, 6, 8, 168, 6, 1,
60+
];
61+
62+
let want = mt::UserStageInfo {
63+
stage_name: "s3://mybucket/data/files".to_string(),
64+
stage_type: mt::StageType::External,
65+
stage_params: mt::StageParams {
66+
storage: StorageParams::S3(StorageS3Config {
67+
bucket: "mybucket".to_string(),
68+
root: "/data/files".to_string(),
69+
access_key_id: "my_key_id".to_string(),
70+
secret_access_key: "my_secret_key".to_string(),
71+
master_key: "my_master_key".to_string(),
72+
..Default::default()
73+
}),
74+
},
75+
file_format_options: mt::FileFormatOptions {
76+
format: mt::StageFileFormatType::Json,
77+
skip_header: 1024,
78+
field_delimiter: "|".to_string(),
79+
record_delimiter: "//".to_string(),
80+
compression: mt::StageFileCompression::Bz2,
81+
},
82+
copy_options: mt::CopyOptions {
83+
on_error: mt::OnErrorMode::SkipFileNum(666),
84+
size_limit: 1038,
85+
purge: true,
86+
},
87+
comment: "test".to_string(),
88+
..Default::default()
89+
};
90+
91+
common::test_load_old(func_name!(), user_stage_s3_v8.as_slice(), want)?;
92+
Ok(())
93+
}
94+
4695
#[test]
4796
fn test_user_stage_fs_v6() -> anyhow::Result<()> {
4897
// Encoded data of version 6 of user_stage_fs:

src/meta/protos/proto/config.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ message S3StorageConfig {
2929
string master_key = 7;
3030
bool disable_credential_loader = 8;
3131
bool enable_virtual_host_style = 9;
32+
string security_token = 10;
3233
}
3334

3435
message FsStorageConfig {

src/query/config/src/outer_v0.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,12 @@ pub struct S3StorageConfig {
402402
#[clap(long = "storage-s3-secret-access-key", default_value_t)]
403403
pub secret_access_key: String,
404404

405+
/// Security token for S3 storage
406+
///
407+
/// Check out [documents](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html) for details
408+
#[clap(long = "storage-s3-security-token", default_value_t)]
409+
pub security_token: String,
410+
405411
/// S3 Bucket to use for storage
406412
#[clap(long = "storage-s3-bucket", default_value_t)]
407413
pub bucket: String,
@@ -449,6 +455,7 @@ impl From<InnerStorageS3Config> for S3StorageConfig {
449455
endpoint_url: inner.endpoint_url,
450456
access_key_id: inner.access_key_id,
451457
secret_access_key: inner.secret_access_key,
458+
security_token: inner.security_token,
452459
bucket: inner.bucket,
453460
root: inner.root,
454461
master_key: inner.master_key,
@@ -467,6 +474,7 @@ impl TryInto<InnerStorageS3Config> for S3StorageConfig {
467474
bucket: self.bucket,
468475
access_key_id: self.access_key_id,
469476
secret_access_key: self.secret_access_key,
477+
security_token: self.security_token,
470478
master_key: self.master_key,
471479
root: self.root,
472480
disable_credential_loader: false,

0 commit comments

Comments
 (0)