Coverage Report

Created: 2026-05-23 21:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-store/src/r2_store.rs
Line
Count
Source
1
// Copyright 2026 The NativeLink Authors. All rights reserved.
2
//
3
// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    See LICENSE file for details
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
use core::time::Duration;
16
use std::sync::Arc;
17
18
use aws_config::default_provider::credentials::DefaultCredentialsChain;
19
use aws_config::provider_config::ProviderConfig;
20
use aws_config::{AppName, BehaviorVersion};
21
use aws_sdk_s3::Client;
22
use aws_sdk_s3::config::{Credentials, Region};
23
use nativelink_config::stores::{ExperimentalAwsSpec, ExperimentalR2Spec};
24
use nativelink_error::Error;
25
use nativelink_util::instant_wrapper::InstantWrapper;
26
27
use crate::common_s3_utils::TlsClient;
28
use crate::s3_store::S3Store;
29
30
/// R2-shaped config adapter over [`S3Store`]. Builds an S3 SDK client
31
/// pointed at R2 (custom endpoint, region `auto`, optional explicit
32
/// credentials) and hands it to `S3Store::new_with_client_and_jitter`.
33
#[derive(Debug, Clone, Copy)]
34
pub struct R2Store;
35
36
impl R2Store {
37
0
    pub async fn new<I, NowFn>(
38
0
        spec: &ExperimentalR2Spec,
39
0
        now_fn: NowFn,
40
0
    ) -> Result<Arc<S3Store<NowFn>>, Error>
41
0
    where
42
0
        I: InstantWrapper,
43
0
        NowFn: Fn() -> I + Send + Sync + Unpin + 'static,
44
0
    {
45
0
        let aws_spec = Self::build_aws_spec(spec);
46
0
        let jitter_fn = spec.common.retry.make_jitter_fn();
47
48
0
        let http_client = TlsClient::new(&spec.common.clone());
49
0
        let endpoint = Self::derive_endpoint(spec);
50
51
0
        let mut config_loader = aws_config::defaults(BehaviorVersion::latest())
52
0
            .app_name(AppName::new("nativelink").expect("valid app name"))
53
0
            .timeout_config(
54
0
                aws_config::timeout::TimeoutConfig::builder()
55
0
                    .connect_timeout(Duration::from_secs(15))
56
0
                    .build(),
57
            )
58
0
            .region(Region::new("auto"))
59
0
            .endpoint_url(&endpoint)
60
0
            .http_client(http_client.clone());
61
62
0
        config_loader =
63
0
            match (&spec.access_key_id, &spec.secret_access_key) {
64
0
                (Some(key_id), Some(secret)) => config_loader.credentials_provider(
65
0
                    Credentials::new(key_id, secret, None, None, "r2-explicit"),
66
                ),
67
                _ => {
68
0
                    let default_chain = DefaultCredentialsChain::builder()
69
0
                        .configure(
70
0
                            ProviderConfig::without_region()
71
0
                                .with_region(Some(Region::new("auto")))
72
0
                                .with_http_client(http_client),
73
0
                        )
74
0
                        .build()
75
0
                        .await;
76
0
                    config_loader.credentials_provider(default_chain)
77
                }
78
            };
79
80
0
        let config = config_loader.load().await;
81
0
        let s3_client = Client::new(&config);
82
83
0
        S3Store::new_with_client_and_jitter(&aws_spec, s3_client, jitter_fn, now_fn)
84
0
    }
85
86
1
    pub fn derive_endpoint(spec: &ExperimentalR2Spec) -> String {
87
1
        format!("https://{}.r2.cloudflarestorage.com", spec.account_id)
88
1
    }
89
90
1
    pub fn build_aws_spec(spec: &ExperimentalR2Spec) -> ExperimentalAwsSpec {
91
1
        ExperimentalAwsSpec {
92
1
            region: "auto".to_string(),
93
1
            bucket: spec.bucket.clone(),
94
1
            common: spec.common.clone(),
95
1
        }
96
1
    }
97
}