Coverage Report

Created: 2026-07-21 15:28

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
    #[allow(clippy::new_ret_no_self)] // Because usually everyone returns themselves
38
0
    pub async fn new<I, NowFn>(
39
0
        spec: &ExperimentalR2Spec,
40
0
        now_fn: NowFn,
41
0
    ) -> Result<Arc<S3Store<NowFn>>, Error>
42
0
    where
43
0
        I: InstantWrapper,
44
0
        NowFn: Fn() -> I + Send + Sync + Unpin + 'static,
45
0
    {
46
0
        let aws_spec = Self::build_aws_spec(spec);
47
0
        let jitter_fn = spec.common.retry.make_jitter_fn();
48
49
0
        let http_client = TlsClient::new(&spec.common.clone());
50
0
        let endpoint = Self::derive_endpoint(spec);
51
52
0
        let mut config_loader = aws_config::defaults(BehaviorVersion::latest())
53
0
            .app_name(AppName::new("nativelink").expect("valid app name"))
54
0
            .timeout_config(
55
0
                aws_config::timeout::TimeoutConfig::builder()
56
0
                    .connect_timeout(Duration::from_secs(15))
57
0
                    .build(),
58
            )
59
0
            .region(Region::new("auto"))
60
0
            .endpoint_url(&endpoint)
61
0
            .http_client(http_client.clone());
62
63
0
        config_loader = if let Some(key_id) = &spec.access_key_id
64
0
            && let Some(secret) = &spec.secret_access_key
65
        {
66
0
            config_loader.credentials_provider(Credentials::new(
67
0
                key_id,
68
0
                secret,
69
0
                None,
70
0
                None,
71
                "r2-explicit",
72
            ))
73
        } else {
74
0
            let default_chain = DefaultCredentialsChain::builder()
75
0
                .configure(
76
0
                    ProviderConfig::without_region()
77
0
                        .with_region(Some(Region::new("auto")))
78
0
                        .with_http_client(http_client),
79
0
                )
80
0
                .build()
81
0
                .await;
82
0
            config_loader.credentials_provider(default_chain)
83
        };
84
85
0
        let config = config_loader.load().await;
86
0
        let s3_client = Client::new(&config);
87
88
0
        S3Store::new_with_client_and_jitter(&aws_spec, s3_client, jitter_fn, now_fn)
89
0
    }
90
91
1
    pub fn derive_endpoint(spec: &ExperimentalR2Spec) -> String {
92
1
        format!("https://{}.r2.cloudflarestorage.com", spec.account_id)
93
1
    }
94
95
1
    pub fn build_aws_spec(spec: &ExperimentalR2Spec) -> ExperimentalAwsSpec {
96
1
        ExperimentalAwsSpec {
97
1
            region: "auto".to_string(),
98
1
            bucket: spec.bucket.clone(),
99
1
            common: spec.common.clone(),
100
1
        }
101
1
    }
102
}