Skip to content

Commit 815a24e

Browse files
authored
Merge pull request #6787 from Turbo87/index-storage
Use `Storage` system to sync to the sparse index
2 parents fd30316 + cf85d12 commit 815a24e

File tree

55 files changed

+140
-8362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+140
-8362
lines changed

src/admin/upload_index.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use crate::admin::dialoguer;
2+
use crate::storage::Storage;
3+
use anyhow::Context;
24
use crates_io_index::{Repository, RepositoryConfig};
35
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
4-
use reqwest::blocking::Client;
5-
6-
use crate::config;
76

87
#[derive(clap::Parser, Debug)]
98
#[command(
@@ -16,9 +15,7 @@ pub struct Opts {
1615
}
1716

1817
pub fn run(opts: Opts) -> anyhow::Result<()> {
19-
let config = config::Base::from_environment();
20-
let uploader = config.uploader();
21-
let client = Client::new();
18+
let storage = Storage::from_environment();
2219

2320
println!("fetching git repo");
2421
let config = RepositoryConfig::from_environment();
@@ -32,6 +29,12 @@ pub fn run(opts: Opts) -> anyhow::Result<()> {
3229
return Ok(());
3330
}
3431

32+
let rt = tokio::runtime::Builder::new_current_thread()
33+
.enable_all()
34+
.build()
35+
.context("Failed to initialize tokio runtime")
36+
.unwrap();
37+
3538
let pb = ProgressBar::new(files.len() as u64);
3639
pb.set_style(ProgressStyle::with_template("{bar:60} ({pos}/{len}, ETA {eta})").unwrap());
3740

@@ -44,7 +47,7 @@ pub fn run(opts: Opts) -> anyhow::Result<()> {
4447
}
4548

4649
let contents = std::fs::read_to_string(&path)?;
47-
uploader.upload_index(&client, crate_name, contents)?;
50+
rt.block_on(storage.sync_index(crate_name, Some(contents)))?;
4851
}
4952

5053
println!(

src/storage.rs

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use object_store::aws::{AmazonS3, AmazonS3Builder};
1111
use object_store::local::LocalFileSystem;
1212
use object_store::memory::InMemory;
1313
use object_store::path::Path;
14+
use object_store::prefix::PrefixStore;
1415
use object_store::{ClientOptions, ObjectStore, Result};
1516
use secrecy::{ExposeSecret, SecretString};
1617
use std::fs;
@@ -20,13 +21,15 @@ const PREFIX_CRATES: &str = "crates";
2021
const PREFIX_READMES: &str = "readmes";
2122
const DEFAULT_REGION: &str = "us-west-1";
2223
const CONTENT_TYPE_CRATE: &str = "application/gzip";
24+
const CONTENT_TYPE_INDEX: &str = "text/plain";
2325
const CONTENT_TYPE_README: &str = "text/html";
2426
const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
27+
const CACHE_CONTROL_INDEX: &str = "public,max-age=600";
2528
const CACHE_CONTROL_README: &str = "public,max-age=604800";
2629

2730
#[derive(Debug)]
2831
pub enum StorageConfig {
29-
S3(S3Config),
32+
S3 { default: S3Config, index: S3Config },
3033
LocalFileSystem { path: PathBuf },
3134
InMemory,
3235
}
@@ -43,16 +46,28 @@ impl StorageConfig {
4346
pub fn from_environment() -> Self {
4447
if let Ok(bucket) = dotenvy::var("S3_BUCKET") {
4548
let region = dotenvy::var("S3_REGION").ok();
49+
50+
let index_bucket = env("S3_INDEX_BUCKET");
51+
let index_region = dotenvy::var("S3_INDEX_REGION").ok();
52+
4653
let access_key = env("AWS_ACCESS_KEY");
47-
let secret_key = env("AWS_SECRET_KEY").into();
48-
let s3 = S3Config {
54+
let secret_key: SecretString = env("AWS_SECRET_KEY").into();
55+
56+
let default = S3Config {
4957
bucket,
5058
region,
59+
access_key: access_key.clone(),
60+
secret_key: secret_key.clone(),
61+
};
62+
63+
let index = S3Config {
64+
bucket: index_bucket,
65+
region: index_region,
5166
access_key,
5267
secret_key,
5368
};
5469

55-
return Self::S3(s3);
70+
return Self::S3 { default, index };
5671
}
5772

5873
let current_dir = std::env::current_dir()
@@ -69,6 +84,9 @@ pub struct Storage {
6984
store: Box<dyn ObjectStore>,
7085
crate_upload_store: Box<dyn ObjectStore>,
7186
readme_upload_store: Box<dyn ObjectStore>,
87+
88+
index_store: Box<dyn ObjectStore>,
89+
index_upload_store: Box<dyn ObjectStore>,
7290
}
7391

7492
impl Storage {
@@ -78,48 +96,70 @@ impl Storage {
7896

7997
pub fn from_config(config: &StorageConfig) -> Self {
8098
match config {
81-
StorageConfig::S3(s3) => {
99+
StorageConfig::S3 { default, index } => {
82100
let options = ClientOptions::default();
83-
let store = build_s3(s3, options);
101+
let store = build_s3(default, options);
84102

85103
let options = client_options(CONTENT_TYPE_CRATE, CACHE_CONTROL_IMMUTABLE);
86-
let crate_upload_store = build_s3(s3, options);
104+
let crate_upload_store = build_s3(default, options);
87105

88106
let options = client_options(CONTENT_TYPE_README, CACHE_CONTROL_README);
89-
let readme_upload_store = build_s3(s3, options);
107+
let readme_upload_store = build_s3(default, options);
108+
109+
let options = ClientOptions::default();
110+
let index_store = build_s3(index, options);
111+
112+
let options = client_options(CONTENT_TYPE_INDEX, CACHE_CONTROL_INDEX);
113+
let index_upload_store = build_s3(index, options);
90114

91115
Self {
92116
store: Box::new(store),
93117
crate_upload_store: Box::new(crate_upload_store),
94118
readme_upload_store: Box::new(readme_upload_store),
119+
index_store: Box::new(index_store),
120+
index_upload_store: Box::new(index_upload_store),
95121
}
96122
}
97123

98124
StorageConfig::LocalFileSystem { path } => {
99-
fs::create_dir_all(path)
100-
.context("Failed to create `local_uploads` directory")
125+
warn!(?path, "Using local file system for file storage");
126+
127+
let index_path = path.join("index");
128+
129+
fs::create_dir_all(&index_path)
130+
.context("Failed to create file storage directories")
101131
.unwrap();
102132

103-
warn!(?path, "Using local file system for file storage");
104133
let local = LocalFileSystem::new_with_prefix(path)
105134
.context("Failed to initialize local file system storage")
106135
.unwrap();
107136

137+
let local_index = LocalFileSystem::new_with_prefix(index_path)
138+
.context("Failed to initialize local file system storage")
139+
.unwrap();
140+
108141
let store = ArcStore::new(local);
142+
let index_store = ArcStore::new(local_index);
143+
109144
Self {
110145
store: Box::new(store.clone()),
111146
crate_upload_store: Box::new(store.clone()),
112147
readme_upload_store: Box::new(store),
148+
index_store: Box::new(index_store.clone()),
149+
index_upload_store: Box::new(index_store),
113150
}
114151
}
115152

116153
StorageConfig::InMemory => {
117154
warn!("Using in-memory file storage");
118155
let store = ArcStore::new(InMemory::new());
156+
119157
Self {
120158
store: Box::new(store.clone()),
121159
crate_upload_store: Box::new(store.clone()),
122-
readme_upload_store: Box::new(store),
160+
readme_upload_store: Box::new(store.clone()),
161+
index_store: Box::new(PrefixStore::new(store.clone(), "index")),
162+
index_upload_store: Box::new(PrefixStore::new(store, "index")),
123163
}
124164
}
125165
}
@@ -173,6 +213,16 @@ impl Storage {
173213
self.readme_upload_store.put(&path, bytes).await
174214
}
175215

216+
#[instrument(skip(self))]
217+
pub async fn sync_index(&self, name: &str, content: Option<String>) -> Result<()> {
218+
let path = crates_io_index::Repository::relative_index_file_for_url(name).into();
219+
if let Some(content) = content {
220+
self.index_upload_store.put(&path, content.into()).await
221+
} else {
222+
self.index_store.delete(&path).await
223+
}
224+
}
225+
176226
/// This should only be used for assertions in the test suite!
177227
pub fn as_inner(&self) -> &dyn ObjectStore {
178228
&self.store
@@ -358,4 +408,21 @@ mod tests {
358408
];
359409
assert_eq!(stored_files(&s.store).await, expected_files);
360410
}
411+
412+
#[tokio::test]
413+
async fn sync_index() {
414+
let s = Storage::from_config(&StorageConfig::InMemory);
415+
416+
assert!(stored_files(&s.store).await.is_empty());
417+
418+
let content = "foo".to_string();
419+
s.sync_index("foo", Some(content)).await.unwrap();
420+
421+
let expected_files = vec!["index/3/f/foo"];
422+
assert_eq!(stored_files(&s.store).await, expected_files);
423+
424+
s.sync_index("foo", None).await.unwrap();
425+
426+
assert!(stored_files(&s.store).await.is_empty());
427+
}
361428
}

src/tests/http-data/krate_publish_features_version_2.json

Lines changed: 0 additions & 85 deletions
This file was deleted.

0 commit comments

Comments
 (0)