-
Notifications
You must be signed in to change notification settings - Fork 722
Expand file tree
/
Copy pathconfig.rs
More file actions
505 lines (469 loc) · 19.3 KB
/
config.rs
File metadata and controls
505 lines (469 loc) · 19.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::fmt::Debug;
use opendal_core::Configurator;
use opendal_core::OperatorUri;
use opendal_core::Result;
use serde::Deserialize;
use serde::Serialize;
use crate::backend::S3Builder;
/// Config for Aws S3 and compatible services (including minio, digitalocean space,
/// Tencent Cloud Object Storage(COS) and so on) support.
#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
#[non_exhaustive]
pub struct S3Config {
/// root of this backend.
///
/// All operations will happen under this root.
///
/// default to `/` if not set.
pub root: Option<String>,
/// bucket name of this backend.
///
/// required.
#[serde(alias = "aws_bucket", alias = "aws_bucket_name", alias = "bucket_name")]
pub bucket: String,
/// is bucket versioning enabled for this bucket
pub enable_versioning: bool,
/// endpoint of this backend.
///
/// Endpoint must be full uri, e.g.
///
/// - AWS S3: `https://s3.amazonaws.com` or `https://s3.{region}.amazonaws.com`
/// - Cloudflare R2: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`
/// - Aliyun OSS: `https://{region}.aliyuncs.com`
/// - Tencent COS: `https://cos.{region}.myqcloud.com`
/// - Minio: `http://127.0.0.1:9000`
///
/// If user inputs endpoint without scheme like "s3.amazonaws.com", we
/// will prepend "https://" before it.
///
/// - If endpoint is set, we will take user's input first.
/// - If not, we will try to load it from environment.
/// - If still not set, default to `https://s3.amazonaws.com`.
#[serde(
alias = "aws_endpoint",
alias = "aws_endpoint_url",
alias = "endpoint_url"
)]
pub endpoint: Option<String>,
/// Region represent the signing region of this endpoint. This is required
/// if you are using the default AWS S3 endpoint.
///
/// If using a custom endpoint,
/// - If region is set, we will take user's input first.
/// - If not, we will try to load it from environment.
#[serde(alias = "aws_region")]
pub region: Option<String>,
/// access_key_id of this backend.
///
/// - If access_key_id is set, we will take user's input first.
/// - If not, we will try to load it from environment.
#[serde(alias = "aws_access_key_id")]
pub access_key_id: Option<String>,
/// secret_access_key of this backend.
///
/// - If secret_access_key is set, we will take user's input first.
/// - If not, we will try to load it from environment.
#[serde(alias = "aws_secret_access_key")]
pub secret_access_key: Option<String>,
/// session_token (aka, security token) of this backend.
///
/// This token will expire after sometime, it's recommended to set session_token
/// by hand.
#[serde(alias = "aws_session_token", alias = "aws_token", alias = "token")]
pub session_token: Option<String>,
/// role_arn for this backend.
///
/// If `role_arn` is set, we will use already known config as source
/// credential to assume role with `role_arn`.
pub role_arn: Option<String>,
/// external_id for this backend.
pub external_id: Option<String>,
/// role_session_name for this backend.
pub role_session_name: Option<String>,
/// Disable config load so that opendal will not load config from
/// environment.
///
/// For examples:
///
/// - envs like `AWS_ACCESS_KEY_ID`
/// - files like `~/.aws/config`
pub disable_config_load: bool,
/// Disable load credential from ec2 metadata.
///
/// This option is used to disable the default behavior of opendal
/// to load credential from ec2 metadata, a.k.a., IMDSv2
pub disable_ec2_metadata: bool,
/// Allow anonymous will allow opendal to send request without signing
/// when credential is not loaded.
pub allow_anonymous: bool,
/// server_side_encryption for this backend.
///
/// Available values: `AES256`, `aws:kms`.
#[serde(alias = "aws_server_side_encryption")]
pub server_side_encryption: Option<String>,
/// server_side_encryption_aws_kms_key_id for this backend
///
/// - If `server_side_encryption` set to `aws:kms`, and `server_side_encryption_aws_kms_key_id`
/// is not set, S3 will use aws managed kms key to encrypt data.
/// - If `server_side_encryption` set to `aws:kms`, and `server_side_encryption_aws_kms_key_id`
/// is a valid kms key id, S3 will use the provided kms key to encrypt data.
/// - If the `server_side_encryption_aws_kms_key_id` is invalid or not found, an error will be
/// returned.
/// - If `server_side_encryption` is not `aws:kms`, setting `server_side_encryption_aws_kms_key_id`
/// is a noop.
#[serde(alias = "aws_sse_kms_key_id")]
pub server_side_encryption_aws_kms_key_id: Option<String>,
/// server_side_encryption_customer_algorithm for this backend.
///
/// Available values: `AES256`.
pub server_side_encryption_customer_algorithm: Option<String>,
/// server_side_encryption_customer_key for this backend.
///
/// Value: BASE64-encoded key that matches algorithm specified in
/// `server_side_encryption_customer_algorithm`.
#[serde(alias = "aws_sse_customer_key_base64")]
pub server_side_encryption_customer_key: Option<String>,
/// Set server_side_encryption_customer_key_md5 for this backend.
///
/// Value: MD5 digest of key specified in `server_side_encryption_customer_key`.
pub server_side_encryption_customer_key_md5: Option<String>,
/// default storage_class for this backend.
///
/// Available values:
/// - `DEEP_ARCHIVE`
/// - `GLACIER`
/// - `GLACIER_IR`
/// - `INTELLIGENT_TIERING`
/// - `ONEZONE_IA`
/// - `EXPRESS_ONEZONE`
/// - `OUTPOSTS`
/// - `REDUCED_REDUNDANCY`
/// - `STANDARD`
/// - `STANDARD_IA`
///
/// S3 compatible services don't support all of them
pub default_storage_class: Option<String>,
/// Enable virtual host style so that opendal will send API requests
/// in virtual host style instead of path style.
///
/// - By default, opendal will send API to `https://s3.us-east-1.amazonaws.com/bucket_name`
/// - Enabled, opendal will send API to `https://bucket_name.s3.us-east-1.amazonaws.com`
#[serde(
alias = "aws_virtual_hosted_style_request",
alias = "virtual_hosted_style_request"
)]
pub enable_virtual_host_style: bool,
/// Set maximum batch operations of this backend.
///
/// Some compatible services have a limit on the number of operations in a batch request.
/// For example, R2 could return `Internal Error` while batch delete 1000 files.
///
/// Please tune this value based on services' document.
#[deprecated(
since = "0.52.0",
note = "Please use `delete_max_size` instead of `batch_max_operations`"
)]
pub batch_max_operations: Option<usize>,
/// Set the maximum delete size of this backend.
///
/// Some compatible services have a limit on the number of operations in a batch request.
/// For example, R2 could return `Internal Error` while batch delete 1000 files.
///
/// Please tune this value based on services' document.
pub delete_max_size: Option<usize>,
/// Disable stat with override so that opendal will not send stat request with override queries.
///
/// For example, R2 doesn't support stat with `response_content_type` query.
pub disable_stat_with_override: bool,
/// Checksum Algorithm to use when sending checksums in HTTP headers.
/// This is necessary when writing to AWS S3 Buckets with Object Lock enabled for example.
///
/// Available options:
/// - "crc32c"
/// - "md5"
#[serde(alias = "aws_checksum_algorithm")]
pub checksum_algorithm: Option<String>,
/// Disable write with if match so that opendal will not send write request with if match headers.
///
/// For example, Ceph RADOS S3 doesn't support write with if matched.
pub disable_write_with_if_match: bool,
/// Enable write with append so that opendal will send write request with append headers.
pub enable_write_with_append: bool,
/// OpenDAL uses List Objects V2 by default to list objects.
/// However, some legacy services do not yet support V2.
/// This option allows users to switch back to the older List Objects V1.
pub disable_list_objects_v2: bool,
/// Indicates whether the client agrees to pay for the requests made to the S3 bucket.
#[serde(alias = "aws_request_payer", alias = "request_payer")]
pub enable_request_payer: bool,
/// Container credentials relative URI for ECS Task IAM roles.
///
/// Used in ECS environments where the base metadata endpoint is known.
/// The relative URI is appended to the default ECS endpoint (169.254.170.2).
///
/// Example: "/v2/credentials/my-role-name"
#[serde(alias = "aws_container_credentials_relative_uri")]
pub container_credentials_relative_uri: Option<String>,
/// Container credentials full endpoint for EKS Pod Identity, Fargate, or custom setups.
///
/// Complete URL for fetching credentials. Used in:
/// - EKS Pod Identity environments
/// - AWS Fargate environments
/// - Custom container credential endpoints
///
/// Example: "http://169.254.170.2/v2/credentials/my-role"
#[serde(
alias = "container_credentials_full_uri",
alias = "aws_container_credentials_full_uri"
)]
pub container_credentials_endpoint: Option<String>,
/// Authorization token for container credentials requests.
///
/// Token used for authenticating with the container credentials endpoint.
/// This is an alternative to `container_authorization_token_file`.
#[serde(alias = "aws_container_authorization_token")]
pub container_authorization_token: Option<String>,
/// Path to file containing authorization token for container credentials.
///
/// File should contain the authorization token for authenticating with the
/// container credentials endpoint. Required for EKS Pod Identity.
///
/// This is an alternative to `container_authorization_token`.
#[serde(alias = "aws_container_authorization_token_file")]
pub container_authorization_token_file: Option<String>,
/// Override for the container metadata URI base endpoint.
///
/// Used to override the default http://169.254.170.2 endpoint.
/// Typically used for testing or custom container credential setups.
#[serde(
alias = "aws_container_metadata_uri_override",
alias = "aws_metadata_endpoint",
alias = "metadata_endpoint"
)]
pub container_metadata_uri_override: Option<String>,
}
impl Debug for S3Config {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("S3Config")
.field("root", &self.root)
.field("bucket", &self.bucket)
.field("endpoint", &self.endpoint)
.field("region", &self.region)
.finish_non_exhaustive()
}
}
impl Configurator for S3Config {
type Builder = S3Builder;
fn from_uri(uri: &OperatorUri) -> Result<Self> {
let mut map = uri.options().clone();
if let Some(name) = uri.name() {
map.insert("bucket".to_string(), name.to_string());
}
if let Some(root) = uri.root() {
map.insert("root".to_string(), root.to_string());
}
Self::from_iter(map)
}
#[allow(deprecated)]
fn into_builder(self) -> Self::Builder {
S3Builder {
config: self,
credential_providers: None,
}
}
}
#[cfg(test)]
mod tests {
use std::iter;
use super::*;
use opendal_core::Configurator;
use opendal_core::OperatorUri;
#[test]
fn test_s3_config_original_field_names() {
let json = r#"{
"bucket": "test-bucket",
"access_key_id": "test-key",
"secret_access_key": "test-secret",
"region": "us-west-2",
"endpoint": "https://s3.amazonaws.com",
"session_token": "test-token"
}"#;
let config: S3Config = serde_json::from_str(json).unwrap();
assert_eq!(config.bucket, "test-bucket");
assert_eq!(config.access_key_id, Some("test-key".to_string()));
assert_eq!(config.secret_access_key, Some("test-secret".to_string()));
assert_eq!(config.region, Some("us-west-2".to_string()));
assert_eq!(
config.endpoint,
Some("https://s3.amazonaws.com".to_string())
);
assert_eq!(config.session_token, Some("test-token".to_string()));
}
#[test]
fn test_s3_config_aws_prefixed_aliases() {
let json = r#"{
"aws_bucket": "test-bucket",
"aws_access_key_id": "test-key",
"aws_secret_access_key": "test-secret",
"aws_region": "us-west-2",
"aws_endpoint": "https://s3.amazonaws.com",
"aws_session_token": "test-token"
}"#;
let config: S3Config = serde_json::from_str(json).unwrap();
assert_eq!(config.bucket, "test-bucket");
assert_eq!(config.access_key_id, Some("test-key".to_string()));
assert_eq!(config.secret_access_key, Some("test-secret".to_string()));
assert_eq!(config.region, Some("us-west-2".to_string()));
assert_eq!(
config.endpoint,
Some("https://s3.amazonaws.com".to_string())
);
assert_eq!(config.session_token, Some("test-token".to_string()));
}
#[test]
fn test_s3_config_additional_aliases() {
let json = r#"{
"bucket_name": "test-bucket",
"token": "test-token",
"endpoint_url": "https://s3.amazonaws.com",
"virtual_hosted_style_request": true,
"aws_checksum_algorithm": "crc32c",
"request_payer": true
}"#;
let config: S3Config = serde_json::from_str(json).unwrap();
assert_eq!(config.bucket, "test-bucket");
assert_eq!(config.session_token, Some("test-token".to_string()));
assert_eq!(
config.endpoint,
Some("https://s3.amazonaws.com".to_string())
);
assert!(config.enable_virtual_host_style);
assert_eq!(config.checksum_algorithm, Some("crc32c".to_string()));
assert!(config.enable_request_payer);
}
#[test]
fn test_s3_config_encryption_aliases() {
let json = r#"{
"bucket": "test-bucket",
"aws_server_side_encryption": "aws:kms",
"aws_sse_kms_key_id": "test-kms-key",
"aws_sse_customer_key_base64": "dGVzdC1jdXN0b21lci1rZXk="
}"#;
let config: S3Config = serde_json::from_str(json).unwrap();
assert_eq!(config.bucket, "test-bucket");
assert_eq!(config.server_side_encryption, Some("aws:kms".to_string()));
assert_eq!(
config.server_side_encryption_aws_kms_key_id,
Some("test-kms-key".to_string())
);
assert_eq!(
config.server_side_encryption_customer_key,
Some("dGVzdC1jdXN0b21lci1rZXk=".to_string())
);
}
#[test]
fn from_uri_extracts_bucket_and_root() {
let uri = OperatorUri::new("s3://example-bucket/path/to/root", iter::empty()).unwrap();
let cfg = S3Config::from_uri(&uri).unwrap();
assert_eq!(cfg.bucket, "example-bucket");
assert_eq!(cfg.root.as_deref(), Some("path/to/root"));
}
#[test]
fn from_uri_extracts_endpoint() {
let uri = OperatorUri::new(
"s3://example-bucket/path/to/root?endpoint=https%3A%2F%2Fcustom-s3-endpoint.com",
iter::empty(),
)
.unwrap();
let cfg = S3Config::from_uri(&uri).unwrap();
assert_eq!(cfg.bucket, "example-bucket");
assert_eq!(cfg.root.as_deref(), Some("path/to/root"));
assert_eq!(
cfg.endpoint.as_deref(),
Some("https://custom-s3-endpoint.com")
);
}
#[test]
fn test_s3_config_container_credentials() {
let json = r#"{
"bucket": "test-bucket",
"container_credentials_relative_uri": "/v2/credentials/my-role",
"container_credentials_full_uri": "http://169.254.170.2/v2/credentials/my-role",
"container_authorization_token": "test-token",
"container_authorization_token_file": "/path/to/token",
"container_metadata_uri_override": "http://custom-endpoint"
}"#;
let config: S3Config = serde_json::from_str(json).unwrap();
assert_eq!(config.bucket, "test-bucket");
assert_eq!(
config.container_credentials_relative_uri,
Some("/v2/credentials/my-role".to_string())
);
assert_eq!(
config.container_credentials_endpoint,
Some("http://169.254.170.2/v2/credentials/my-role".to_string())
);
assert_eq!(
config.container_authorization_token,
Some("test-token".to_string())
);
assert_eq!(
config.container_authorization_token_file,
Some("/path/to/token".to_string())
);
assert_eq!(
config.container_metadata_uri_override,
Some("http://custom-endpoint".to_string())
);
}
#[test]
fn test_s3_config_container_credentials_aws_aliases() {
let json = r#"{
"bucket": "test-bucket",
"aws_container_credentials_relative_uri": "/v2/credentials/my-role",
"aws_container_credentials_full_uri": "http://169.254.170.2/v2/credentials/my-role",
"aws_container_authorization_token": "test-token",
"aws_container_authorization_token_file": "/path/to/token",
"aws_container_metadata_uri_override": "http://custom-endpoint"
}"#;
let config: S3Config = serde_json::from_str(json).unwrap();
assert_eq!(config.bucket, "test-bucket");
assert_eq!(
config.container_credentials_relative_uri,
Some("/v2/credentials/my-role".to_string())
);
assert_eq!(
config.container_credentials_endpoint,
Some("http://169.254.170.2/v2/credentials/my-role".to_string())
);
assert_eq!(
config.container_authorization_token,
Some("test-token".to_string())
);
assert_eq!(
config.container_authorization_token_file,
Some("/path/to/token".to_string())
);
assert_eq!(
config.container_metadata_uri_override,
Some("http://custom-endpoint".to_string())
);
}
}