Skip to content

Commit 7f2074f

Browse files
committed
add download endpoint for rustdoc archive
1 parent dd66a73 commit 7f2074f

File tree

15 files changed

+476
-18
lines changed

15 files changed

+476
-18
lines changed

Diff for: src/build_queue.rs

+32-7
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ mod tests {
550550
fn test_invalidate_cdn_after_build_and_error() {
551551
crate::test::wrapper(|env| {
552552
env.override_config(|config| {
553-
config.cloudfront_distribution_id_web = Some("distribution_id".into());
553+
config.cloudfront_distribution_id_web = Some("distribution_id_web".into());
554+
config.cloudfront_distribution_id_static = Some("distribution_id_static".into());
554555
});
555556

556557
let queue = env.build_queue();
@@ -573,8 +574,16 @@ mod tests {
573574
assert_eq!(
574575
*ir,
575576
[
576-
("distribution_id".into(), "/will_succeed*".into()),
577-
("distribution_id".into(), "/crate/will_succeed*".into()),
577+
("distribution_id_web".into(), "/will_succeed*".into()),
578+
("distribution_id_web".into(), "/crate/will_succeed*".into()),
579+
(
580+
"distribution_id_static".into(),
581+
"/rustdoc/will_succeed*".into()
582+
),
583+
(
584+
"distribution_id_static".into(),
585+
"/sources/will_succeed*".into()
586+
),
578587
]
579588
);
580589
}
@@ -588,10 +597,26 @@ mod tests {
588597
assert_eq!(
589598
*ir,
590599
[
591-
("distribution_id".into(), "/will_succeed*".into()),
592-
("distribution_id".into(), "/crate/will_succeed*".into()),
593-
("distribution_id".into(), "/will_fail*".into()),
594-
("distribution_id".into(), "/crate/will_fail*".into()),
600+
("distribution_id_web".into(), "/will_succeed*".into()),
601+
("distribution_id_web".into(), "/crate/will_succeed*".into()),
602+
(
603+
"distribution_id_static".into(),
604+
"/rustdoc/will_succeed*".into()
605+
),
606+
(
607+
"distribution_id_static".into(),
608+
"/sources/will_succeed*".into()
609+
),
610+
("distribution_id_web".into(), "/will_fail*".into()),
611+
("distribution_id_web".into(), "/crate/will_fail*".into()),
612+
(
613+
"distribution_id_static".into(),
614+
"/rustdoc/will_fail*".into()
615+
),
616+
(
617+
"distribution_id_static".into(),
618+
"/sources/will_fail*".into()
619+
),
595620
]
596621
);
597622
}

Diff for: src/cdn.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,17 @@ pub(crate) fn invalidate_crate(config: &Config, cdn: &CdnBackend, name: &str) ->
123123
distribution_id,
124124
&[&format!("/{}*", name), &format!("/crate/{}*", name)],
125125
)
126-
.context("error creating CDN invalidation")?;
126+
.context("error creating web CDN invalidation")?;
127+
}
128+
if let Some(distribution_id) = config.cloudfront_distribution_id_static.as_ref() {
129+
cdn.create_invalidation(
130+
distribution_id,
131+
&[
132+
&format!("/rustdoc/{}*", name),
133+
&format!("/sources/{}*", name),
134+
],
135+
)
136+
.context("error creating static CDN invalidation")?;
127137
}
128138

129139
Ok(())
@@ -168,6 +178,32 @@ mod tests {
168178
})
169179
}
170180

181+
#[test]
182+
fn invalidate_a_crate() {
183+
crate::test::wrapper(|env| {
184+
env.override_config(|config| {
185+
config.cloudfront_distribution_id_web = Some("distribution_id_web".into());
186+
config.cloudfront_distribution_id_static = Some("distribution_id_static".into());
187+
});
188+
invalidate_crate(&*env.config(), &*env.cdn(), "krate")?;
189+
190+
assert!(matches!(*env.cdn(), CdnBackend::Dummy(_)));
191+
if let CdnBackend::Dummy(ref invalidation_requests) = *env.cdn() {
192+
let ir = invalidation_requests.lock().unwrap();
193+
assert_eq!(
194+
*ir,
195+
[
196+
("distribution_id_web".into(), "/krate*".into()),
197+
("distribution_id_web".into(), "/crate/krate*".into()),
198+
("distribution_id_static".into(), "/rustdoc/krate*".into()),
199+
("distribution_id_static".into(), "/sources/krate*".into()),
200+
]
201+
);
202+
}
203+
Ok(())
204+
});
205+
}
206+
171207
async fn get_mock_config() -> aws_sdk_cloudfront::Config {
172208
let cfg = aws_config::from_env()
173209
.region(Region::new("eu-central-1"))

Diff for: src/config.rs

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ pub struct Config {
2626
#[cfg(test)]
2727
pub(crate) s3_bucket_is_temporary: bool,
2828

29+
// CloudFront domain which we can access
30+
// public S3 files through
31+
pub(crate) s3_static_domain: String,
32+
2933
// Github authentication
3034
pub(crate) github_accesstoken: Option<String>,
3135
pub(crate) github_updater_min_rate_limit: u32,
@@ -67,6 +71,8 @@ pub struct Config {
6771
// CloudFront distribution ID for the web server.
6872
// Will be used for invalidation-requests.
6973
pub cloudfront_distribution_id_web: Option<String>,
74+
/// same for the `static.docs.rs` distribution
75+
pub cloudfront_distribution_id_static: Option<String>,
7076

7177
// Build params
7278
pub(crate) build_attempts: u16,
@@ -125,6 +131,8 @@ impl Config {
125131
#[cfg(test)]
126132
s3_bucket_is_temporary: false,
127133

134+
s3_static_domain: env("S3_STATIC_DOMAIN", "https://static.docs.rs".to_string())?,
135+
128136
github_accesstoken: maybe_env("DOCSRS_GITHUB_ACCESSTOKEN")?,
129137
github_updater_min_rate_limit: env("DOCSRS_GITHUB_UPDATER_MIN_RATE_LIMIT", 2500)?,
130138

@@ -148,6 +156,7 @@ impl Config {
148156
cdn_backend: env("DOCSRS_CDN_BACKEND", CdnKind::Dummy)?,
149157

150158
cloudfront_distribution_id_web: maybe_env("CLOUDFRONT_DISTRIBUTION_ID_WEB")?,
159+
cloudfront_distribution_id_static: maybe_env("CLOUDFRONT_DISTRIBUTION_ID_STATIC")?,
151160

152161
local_archive_cache_path: env(
153162
"DOCSRS_ARCHIVE_INDEX_CACHE_PATH",

Diff for: src/db/file.rs

+4
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ pub fn add_path_into_remote_archive<P: AsRef<Path>>(
3838
storage: &Storage,
3939
archive_path: &str,
4040
path: P,
41+
public_access: bool,
4142
) -> Result<(Value, CompressionAlgorithm)> {
4243
let (file_list, algorithm) = storage.store_all_in_archive(archive_path, path.as_ref())?;
44+
if public_access {
45+
storage.set_public_access(archive_path, true)?;
46+
}
4347
Ok((
4448
file_list_to_json(file_list.into_iter().collect()),
4549
algorithm,

Diff for: src/db/migrate.rs

+5
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,11 @@ pub fn migrate(version: Option<Version>, conn: &mut Client) -> crate::error::Res
848848
"CREATE INDEX builds_release_id_idx ON builds (rid);",
849849
"DROP INDEX builds_release_id_idx;",
850850
),
851+
sql_migration!(
852+
context, 35, "add public visibility to files table",
853+
"ALTER TABLE files ADD COLUMN public BOOL NOT NULL DEFAULT FALSE;",
854+
"ALTER TABLE files DROP COLUMN public;"
855+
),
851856

852857
];
853858

Diff for: src/docbuilder/rustwide_builder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ impl RustwideBuilder {
410410
&self.storage,
411411
&rustdoc_archive_path(name, version),
412412
local_storage.path(),
413+
true,
413414
)?;
414415
algs.insert(new_alg);
415416
};
@@ -421,6 +422,7 @@ impl RustwideBuilder {
421422
&self.storage,
422423
&source_archive_path(name, version),
423424
build.host_source_dir(),
425+
false,
424426
)?;
425427
algs.insert(new_alg);
426428
files_list

Diff for: src/storage/database.rs

+26
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,32 @@ impl DatabaseBackend {
2121
Ok(conn.query(query, &[&path])?[0].get(0))
2222
}
2323

24+
pub(super) fn get_public_access(&self, path: &str) -> Result<bool> {
25+
match self.pool.get()?.query_opt(
26+
"SELECT public
27+
FROM files
28+
WHERE path = $1",
29+
&[&path],
30+
)? {
31+
Some(row) => Ok(row.get(0)),
32+
None => Err(super::PathNotFoundError.into()),
33+
}
34+
}
35+
36+
pub(super) fn set_public_access(&self, path: &str, public: bool) -> Result<()> {
37+
if self.pool.get()?.execute(
38+
"UPDATE files
39+
SET public = $2
40+
WHERE path = $1",
41+
&[&path, &public],
42+
)? == 1
43+
{
44+
Ok(())
45+
} else {
46+
Err(super::PathNotFoundError.into())
47+
}
48+
}
49+
2450
pub(super) fn get(
2551
&self,
2652
path: &str,

Diff for: src/storage/mod.rs

+55-2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,20 @@ impl Storage {
140140
}
141141
}
142142

143+
pub(crate) fn get_public_access(&self, path: &str) -> Result<bool> {
144+
match &self.backend {
145+
StorageBackend::Database(db) => db.get_public_access(path),
146+
StorageBackend::S3(s3) => s3.get_public_access(path),
147+
}
148+
}
149+
150+
pub(crate) fn set_public_access(&self, path: &str, public: bool) -> Result<()> {
151+
match &self.backend {
152+
StorageBackend::Database(db) => db.set_public_access(path, public),
153+
StorageBackend::S3(s3) => s3.set_public_access(path, public),
154+
}
155+
}
156+
143157
fn max_file_size_for(&self, path: &str) -> usize {
144158
if path.ends_with(".html") {
145159
self.config.max_file_size_html
@@ -620,9 +634,38 @@ mod backend_tests {
620634
Ok(())
621635
}
622636

637+
fn test_set_public(storage: &Storage) -> Result<()> {
638+
let path: &str = "foo/bar.txt";
639+
640+
storage.store_blobs(vec![Blob {
641+
path: path.into(),
642+
mime: "text/plain".into(),
643+
date_updated: Utc::now(),
644+
compression: None,
645+
content: b"test content\n".to_vec(),
646+
}])?;
647+
648+
assert!(!storage.get_public_access(path)?);
649+
storage.set_public_access(path, true)?;
650+
assert!(storage.get_public_access(path)?);
651+
storage.set_public_access(path, false)?;
652+
assert!(!storage.get_public_access(path)?);
653+
654+
for path in &["bar.txt", "baz.txt", "foo/baz.txt"] {
655+
assert!(storage
656+
.set_public_access(path, true)
657+
.unwrap_err()
658+
.downcast_ref::<PathNotFoundError>()
659+
.is_some());
660+
}
661+
662+
Ok(())
663+
}
664+
623665
fn test_get_object(storage: &Storage) -> Result<()> {
666+
let path: &str = "foo/bar.txt";
624667
let blob = Blob {
625-
path: "foo/bar.txt".into(),
668+
path: path.into(),
626669
mime: "text/plain".into(),
627670
date_updated: Utc::now(),
628671
compression: None,
@@ -631,16 +674,25 @@ mod backend_tests {
631674

632675
storage.store_blobs(vec![blob.clone()])?;
633676

634-
let found = storage.get("foo/bar.txt", std::usize::MAX)?;
677+
let found = storage.get(path, std::usize::MAX)?;
635678
assert_eq!(blob.mime, found.mime);
636679
assert_eq!(blob.content, found.content);
637680

681+
// default visibility is private
682+
assert!(!storage.get_public_access(path)?);
683+
638684
for path in &["bar.txt", "baz.txt", "foo/baz.txt"] {
639685
assert!(storage
640686
.get(path, std::usize::MAX)
641687
.unwrap_err()
642688
.downcast_ref::<PathNotFoundError>()
643689
.is_some());
690+
691+
assert!(storage
692+
.get_public_access(path)
693+
.unwrap_err()
694+
.downcast_ref::<PathNotFoundError>()
695+
.is_some());
644696
}
645697

646698
Ok(())
@@ -1028,6 +1080,7 @@ mod backend_tests {
10281080
test_delete_prefix_without_matches,
10291081
test_delete_percent,
10301082
test_exists_without_remote_archive,
1083+
test_set_public,
10311084
}
10321085

10331086
tests_with_metrics {

0 commit comments

Comments
 (0)