Skip to content

Commit 3a663de

Browse files
committed
Invalidate release distribution on Fastly
We are working on serving Rust releases through Fastly as well as CloudFront. The logic to invalidate cached released has therefore been slightly reworked to handle both CDNs. First, new environment variables have been added to configure the Fastly integration. An API token is required as is the domain name that should get invalidated. Second, a temporary environment variable has been added that enables the integration. This enables us to test the integration in the dev environment without requiring any changes to the production environment.
1 parent b55c4b3 commit 3a663de

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

src/config.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::discourse::Discourse;
2+
use crate::fastly::Fastly;
23
use crate::github::Github;
34
use crate::Context;
45
use anyhow::{Context as _, Error};
@@ -192,6 +193,14 @@ pub(crate) struct Config {
192193

193194
/// The app ID associated with the private key being passed.
194195
pub(crate) github_app_id: Option<u32>,
196+
197+
/// An API token for Fastly with the `purge_select` scope.
198+
pub(crate) fastly_api_token: Option<String>,
199+
/// The static domain name that is used with Fastly, e.g. `static.rust-lang.org`.
200+
pub(crate) fastly_static_domain: Option<String>,
201+
202+
/// Temporary variable to test Fastly in the dev environment only.
203+
pub(crate) invalidate_fastly: bool,
195204
}
196205

197206
impl Config {
@@ -226,6 +235,9 @@ impl Config {
226235
discourse_api_key: maybe_env("DISCOURSE_API_KEY")?,
227236
github_app_key: maybe_env("GITHUB_APP_KEY")?,
228237
github_app_id: maybe_env("GITHUB_APP_ID")?,
238+
fastly_api_token: maybe_env("FASTLY_API_TOKEN")?,
239+
fastly_static_domain: maybe_env("FASTLY_STATIC_DOMAIN")?,
240+
invalidate_fastly: bool_env("INVALIDATE_FASTLY")?,
229241
})
230242
}
231243

@@ -248,6 +260,14 @@ impl Config {
248260
}
249261
}
250262

263+
pub(crate) fn fastly(&self) -> Option<Fastly> {
264+
if let (Some(token), Some(domain)) = (&self.fastly_api_token, &self.fastly_static_domain) {
265+
Some(Fastly::new(token.clone(), domain.clone()))
266+
} else {
267+
None
268+
}
269+
}
270+
251271
pub(crate) fn stable_dev_static_blog_contents(
252272
&self,
253273
release: &str,

src/fastly.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use anyhow::Error;
2+
use curl::easy::Easy;
3+
4+
pub struct Fastly {
5+
api_token: String,
6+
domain: String,
7+
client: Easy,
8+
}
9+
10+
impl Fastly {
11+
pub fn new(api_token: String, domain: String) -> Self {
12+
Self {
13+
api_token,
14+
domain,
15+
client: Easy::new(),
16+
}
17+
}
18+
19+
pub fn purge(&mut self, path: &str) -> Result<(), Error> {
20+
self.start_new_request()?;
21+
22+
self.client.post(true)?;
23+
self.client.url(&format!(
24+
"https://api.fastly.com/purge/{}/{}",
25+
self.domain, path
26+
))?;
27+
28+
self.client.perform().map_err(|error| error.into())
29+
}
30+
31+
fn start_new_request(&mut self) -> anyhow::Result<()> {
32+
self.client.reset();
33+
self.client.useragent("rust-lang/promote-release")?;
34+
let mut headers = curl::easy::List::new();
35+
headers.append(&format!("Fastly-Key: {}", self.api_token))?;
36+
headers.append("Content-Type: application/json")?;
37+
self.client.http_headers(headers)?;
38+
Ok(())
39+
}
40+
}

src/main.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod build_manifest;
55
mod config;
66
mod curl_helper;
77
mod discourse;
8+
mod fastly;
89
mod github;
910
mod recompress;
1011
mod sign;
@@ -18,6 +19,7 @@ use std::time::Duration;
1819
use std::{collections::HashSet, env};
1920

2021
use crate::build_manifest::BuildManifest;
22+
use crate::config::{Channel, Config};
2123
use crate::sign::Signer;
2224
use crate::smoke_test::SmokeTester;
2325
use anyhow::Error;
@@ -26,8 +28,6 @@ use curl::easy::Easy;
2628
use fs2::FileExt;
2729
use github::{CreateTag, Github};
2830

29-
use crate::config::{Channel, Config};
30-
3131
const TARGET: &str = env!("TARGET");
3232

3333
const BLOG_PRIMARY_BRANCH: &str = "master";
@@ -557,7 +557,12 @@ impl Context {
557557
}
558558

559559
fn invalidate_releases(&self) -> Result<(), Error> {
560-
self.invalidate_cloudfront(&self.config.cloudfront_static_id, &["/dist/*".into()])
560+
let paths = ["/dist/*".into()];
561+
562+
self.invalidate_cloudfront(&self.config.cloudfront_static_id, &paths)?;
563+
self.invalidate_fastly(&paths)?;
564+
565+
Ok(())
561566
}
562567

563568
fn invalidate_cloudfront(&self, distribution_id: &str, paths: &[String]) -> Result<(), Error> {
@@ -592,6 +597,32 @@ impl Context {
592597
Ok(())
593598
}
594599

600+
fn invalidate_fastly(&self, paths: &[String]) -> Result<(), Error> {
601+
// Fastly invalidations are opt-in while we test the integration
602+
// Set PROMOTE_RELEASE_INVALIDATE_FASTLY=1 to enable them
603+
if !self.config.invalidate_fastly {
604+
return Ok(());
605+
}
606+
607+
let mut fastly = match self.config.fastly() {
608+
Some(fastly) => fastly,
609+
None => {
610+
println!();
611+
println!("WARNING! Skipped Fastly invalidation of: {:?}", paths);
612+
println!("Set PROMOTE_RELEASE_FASTLY_API_TOKEN and PROMOTE_RELEASE_FASTLY_STATIC_DOMAIN if you want to invalidate Fastly");
613+
println!();
614+
return Ok(());
615+
}
616+
};
617+
618+
for path in paths {
619+
let path = path.replace('*', "");
620+
fastly.purge(&path)?;
621+
}
622+
623+
Ok(())
624+
}
625+
595626
fn tag_release(&mut self, rustc_commit: &str, signer: &mut Signer) -> Result<(), Error> {
596627
if self.config.channel != Channel::Stable {
597628
// We don't tag non-stable releases

0 commit comments

Comments
 (0)