move poking services into app boot after systemd notify

Signed-off-by: Christine Dodrill <me@christine.website>
This commit is contained in:
Cadey Ratio 2021-01-16 21:38:22 -05:00
parent 17af42bc69
commit 4bcc848bb1
9 changed files with 286 additions and 18 deletions

67
Cargo.lock generated
View File

@ -212,6 +212,21 @@ version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfcache"
version = "0.1.0"
dependencies = [
"eyre",
"kankyo",
"reqwest",
"serde",
"serde_json",
"thiserror",
"tokio",
"tracing",
"tracing-futures",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
@ -1068,12 +1083,35 @@ dependencies = [
"kernel32-sys",
"libc",
"log",
"miow",
"miow 0.2.2",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "mio-named-pipes"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
dependencies = [
"log",
"mio",
"miow 0.3.6",
"winapi 0.3.9",
]
[[package]]
name = "mio-uds"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
"iovec",
"libc",
"mio",
]
[[package]]
name = "miow"
version = "0.2.2"
@ -1086,6 +1124,16 @@ dependencies = [
"ws2_32-sys",
]
[[package]]
name = "miow"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
"socket2",
"winapi 0.3.9",
]
[[package]]
name = "multipart"
version = "0.17.1"
@ -1913,6 +1961,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074"
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]]
name = "sitemap"
version = "0.4.1"
@ -2069,12 +2126,17 @@ dependencies = [
"futures-core",
"iovec",
"lazy_static",
"libc",
"memchr",
"mio",
"mio-named-pipes",
"mio-uds",
"num_cpus",
"pin-project-lite 0.1.11",
"signal-hook-registry",
"slab",
"tokio-macros",
"winapi 0.3.9",
]
[[package]]
@ -2603,6 +2665,7 @@ checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
name = "xesite"
version = "2.2.0"
dependencies = [
"cfcache",
"chrono",
"color-eyre",
"comrak",
@ -2621,7 +2684,7 @@ dependencies = [
"pfacts",
"pretty_env_logger",
"prometheus",
"rand 0.7.3",
"rand 0.8.1",
"reqwest",
"ructe",
"sdnotify",

View File

@ -21,6 +21,7 @@ log = "0.4"
mime = "0.3.0"
prometheus = { version = "0.10", default-features = false, features = ["process"] }
rand = "0"
reqwest = { version = "0.10", features = ["json"] }
sdnotify = { version = "0.1", default-features = false }
serde_dhall = "0.8.0"
serde = { version = "1", features = ["derive"] }
@ -37,6 +38,7 @@ url = "2"
uuid = { version = "0.8", features = ["serde", "v4"] }
# workspace dependencies
cfcache = { path = "./lib/cfcache" }
go_vanity = { path = "./lib/go_vanity" }
jsonfeed = { path = "./lib/jsonfeed" }
mi = { path = "./lib/mi" }

20
lib/cfcache/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "cfcache"
version = "0.1.0"
authors = ["Christine Dodrill <me@christine.website>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = { version = "0.10", features = ["json"] }
serde_json = "1.0"
serde = { version = "1", features = ["derive"] }
thiserror = "1"
tracing = "0.1"
tracing-futures = "0.2"
[dev-dependencies]
eyre = "0.6.5"
kankyo = "0.3"
tokio = { version = "0.2", features = ["full"] }

View File

@ -0,0 +1,15 @@
use eyre::Result;
#[tokio::main]
async fn main() -> Result<()> {
kankyo::init()?;
let key = std::env::var("CF_TOKEN")?;
let zone_id = std::env::var("CF_ZONE_ID")?;
let cli = cfcache::Client::new(key, zone_id)?;
cli.purge(vec!["https://christine.website/.within/health".to_string()])
.await?;
Ok(())
}

64
lib/cfcache/src/lib.rs Normal file
View File

@ -0,0 +1,64 @@
use reqwest::header;
use tracing::instrument;
pub type Result<T = ()> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
#[error("request error: {0}")]
Request(#[from] reqwest::Error),
#[error("invalid header value: {0}")]
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
}
pub struct Client {
zone_id: String,
cli: reqwest::Client,
}
static USER_AGENT: &str = concat!(
"xesite ",
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION")
);
impl Client {
pub fn new(api_key: String, zone_id: String) -> Result<Self> {
let mut headers = header::HeaderMap::new();
headers.insert(
header::AUTHORIZATION,
header::HeaderValue::from_str(&format!("Bearer {}", api_key))?,
);
let cli = reqwest::Client::builder()
.user_agent(USER_AGENT)
.default_headers(headers)
.build()?;
Ok(Self { zone_id, cli })
}
#[instrument(skip(self), err)]
pub async fn purge(&self, urls: Vec<String>) -> Result {
#[derive(serde::Serialize)]
struct Files {
files: Vec<String>,
}
self.cli
.post(&format!(
"https://api.cloudflare.com/client/v4/zones/{}/purge_cache",
self.zone_id
))
.json(&Files { files: urls })
.send()
.await?
.error_for_status()?;
Ok(())
}
}

View File

@ -34,7 +34,7 @@ impl Client {
})
}
#[instrument(skip(self))]
#[instrument(skip(self), err)]
pub async fn mentioners(&self, url: String) -> Result<Vec<WebMention>> {
Ok(self
.cli
@ -46,6 +46,16 @@ impl Client {
.json()
.await?)
}
#[instrument(skip(self), err)]
pub async fn refresh(&self) -> Result<()> {
self.cli
.post("https://mi.within.website/api/blog/refresh")
.send()
.await?
.error_for_status()?;
Ok(())
}
}
#[derive(Debug, Deserialize, Eq, PartialEq, Clone)]

View File

@ -5,6 +5,7 @@ use std::{fs, path::PathBuf};
use tracing::{error, instrument};
pub mod markdown;
pub mod poke;
#[derive(Clone, Deserialize)]
pub struct Config {

86
src/app/poke.rs Normal file
View File

@ -0,0 +1,86 @@
use color_eyre::eyre::Result;
use std::{env, time::Duration};
use tokio::time::delay_for;
#[instrument(err)]
pub async fn the_cloud() -> Result<()> {
info!("waiting for things to settle");
delay_for(Duration::from_secs(10)).await;
info!("purging cloudflare cache");
cloudflare().await?;
info!("waiting for the cloudflare cache to purge globally");
delay_for(Duration::from_secs(45)).await;
info!("poking mi");
mi().await?;
info!("poking bing");
bing().await?;
info!("poking google");
google().await?;
Ok(())
}
#[instrument(err)]
async fn bing() -> Result<()> {
let cli = reqwest::Client::new();
cli.get("https://www.bing.com/ping")
.query(&[("sitemap", "https://christine.website/sitemap.xml")])
.header("User-Agent", crate::APPLICATION_NAME)
.send()
.await?
.error_for_status()?;
Ok(())
}
#[instrument(err)]
async fn google() -> Result<()> {
let cli = reqwest::Client::new();
cli.get("https://www.google.com/ping")
.query(&[("sitemap", "https://christine.website/sitemap.xml")])
.header("User-Agent", crate::APPLICATION_NAME)
.send()
.await?
.error_for_status()?;
Ok(())
}
#[instrument(err)]
async fn cloudflare() -> Result<()> {
let cli = cfcache::Client::new(env::var("CF_TOKEN")?, env::var("CF_ZONE_ID")?)?;
cli.purge(
vec![
"https://christine.website/sitemap.xml",
"https://christine.website",
"https://christine.website/blog",
"https://christine.website/blog.atom",
"https://christine.website/blog.json",
"https://christine.website/blog.rss",
"https://christine.website/gallery",
"https://christine.website/talks",
"https://christine.website/resume",
"https://christine.website/signalboost",
"https://christine.website/feeds",
]
.into_iter()
.map(|i| i.to_string())
.collect(),
)
.await?;
Ok(())
}
#[instrument(err)]
async fn mi() -> Result<()> {
let cli = mi::Client::new(env::var("MI_TOKEN")?, crate::APPLICATION_NAME.to_string())?;
cli.refresh().await?;
Ok(())
}

View File

@ -39,21 +39,6 @@ async fn main() -> Result<()> {
.await?,
);
match sdnotify::SdNotify::from_env() {
Ok(ref mut n) => {
n.notify_ready().map_err(|why| {
error!("can't signal readiness to systemd: {}", why);
why
})?;
n.set_status(format!("hosting {} posts", state.clone().everything.len()))
.map_err(|why| {
error!("can't signal status to systemd: {}", why);
why
})?;
}
Err(why) => error!("not running under systemd with Type=notify: {}", why),
}
let healthcheck = warp::get().and(warp::path(".within").and(warp::path("health")).map(|| "OK"));
let base = warp::path!("blog" / ..);
@ -222,6 +207,28 @@ async fn main() -> Result<()> {
.with(warp::log(APPLICATION_NAME))
.recover(handlers::rejection);
match sdnotify::SdNotify::from_env() {
Ok(ref mut n) => {
// shitty heuristic for detecting if we're running in prod
tokio::spawn(async {
if let Err(why) = app::poke::the_cloud().await {
error!("Unable to poke the cloud: {}", why);
}
});
n.notify_ready().map_err(|why| {
error!("can't signal readiness to systemd: {}", why);
why
})?;
n.set_status(format!("hosting {} posts", state.clone().everything.len()))
.map_err(|why| {
error!("can't signal status to systemd: {}", why);
why
})?;
}
Err(why) => error!("not running under systemd with Type=notify: {}", why),
}
warp::serve(site)
.run((
[0, 0, 0, 0],