diff --git a/magic-nix-cache/src/flakehub.rs b/magic-nix-cache/src/flakehub.rs index 9a4a93b..c06b244 100644 --- a/magic-nix-cache/src/flakehub.rs +++ b/magic-nix-cache/src/flakehub.rs @@ -1,5 +1,5 @@ use crate::env::Environment; -use crate::error::{self, Error, Result}; +use crate::error::{Error, Result}; use anyhow::Context; use attic::cache::CacheName; use attic::nix_store::{NixStore, StorePath}; @@ -32,28 +32,16 @@ pub struct State { pub async fn init_cache( environment: Environment, flakehub_api_server: &Url, - flakehub_api_server_netrc: &Path, flakehub_cache_server: &Url, flakehub_flake_name: Option, store: Arc, - using_dnixd: bool, + auth_method: &super::FlakeHubAuthSource, ) -> Result { - if using_dnixd { - let dnixd_state_dir: &Path = Path::new(&crate::DETERMINATE_STATE_DIR); - let expected_netrc_path = dnixd_state_dir.join("netrc"); - if flakehub_api_server_netrc != expected_netrc_path { - let err = format!("flakehub-api-server-netrc was ({}), expected ({}) since determinate-nixd is available", flakehub_api_server_netrc.display(), expected_netrc_path.display()); - return Err(error::Error::Config(err)); - } - } // Parse netrc to get the credentials for api.flakehub.com. let netrc = { - let mut netrc_file = File::open(flakehub_api_server_netrc).await.map_err(|e| { - Error::Internal(format!( - "Failed to open {}: {}", - flakehub_api_server_netrc.display(), - e - )) + let netrc_path = auth_method.as_path_buf(); + let mut netrc_file = File::open(&netrc_path).await.map_err(|e| { + Error::Internal(format!("Failed to open {}: {}", netrc_path.display(), e)) })?; let mut netrc_contents = String::new(); netrc_file @@ -62,7 +50,7 @@ pub async fn init_cache( .map_err(|e| { Error::Internal(format!( "Failed to read {} contents: {}", - flakehub_api_server_netrc.display(), + netrc_path.display(), e )) })?; @@ -99,7 +87,7 @@ pub async fn init_cache( )) })?; - if !using_dnixd { + if let super::FlakeHubAuthSource::Netrc(netrc_path) = auth_method { // Append an entry for the FlakeHub cache server to netrc. if !netrc .machines @@ -109,12 +97,12 @@ pub async fn init_cache( let mut netrc_file = tokio::fs::OpenOptions::new() .create(false) .append(true) - .open(flakehub_api_server_netrc) + .open(netrc_path) .await .map_err(|e| { Error::Internal(format!( "Failed to open {} for appending: {}", - flakehub_api_server_netrc.display(), + netrc_path.display(), e )) })?; @@ -131,7 +119,7 @@ pub async fn init_cache( .map_err(|e| { Error::Internal(format!( "Failed to write credentials to {}: {}", - flakehub_api_server_netrc.display(), + netrc_path.display(), e )) })?; @@ -149,23 +137,25 @@ pub async fn init_cache( // Periodically refresh JWT in GitHub Actions environment if environment.is_github_actions() { - // NOTE(cole-h): This is a workaround -- at the time of writing, GitHub Actions JWTs are only - // valid for 5 minutes after being issued. FlakeHub uses these JWTs for authentication, which - // means that after those 5 minutes have passed and the token is expired, FlakeHub (and by - // extension FlakeHub Cache) will no longer allow requests using this token. However, GitHub - // gives us a way to repeatedly request new tokens, so we utilize that and refresh the token - // every 2 minutes (less than half of the lifetime of the token). - let netrc_path_clone = flakehub_api_server_netrc.to_path_buf(); - let initial_github_jwt_clone = flakehub_password.clone(); - let flakehub_cache_server_clone = flakehub_cache_server.to_string(); - let api_clone = api.clone(); - - tokio::task::spawn(refresh_github_actions_jwt_worker( - netrc_path_clone, - initial_github_jwt_clone, - flakehub_cache_server_clone, - api_clone, - )); + if let super::FlakeHubAuthSource::Netrc(path) = auth_method { + // NOTE(cole-h): This is a workaround -- at the time of writing, GitHub Actions JWTs are only + // valid for 5 minutes after being issued. FlakeHub uses these JWTs for authentication, which + // means that after those 5 minutes have passed and the token is expired, FlakeHub (and by + // extension FlakeHub Cache) will no longer allow requests using this token. However, GitHub + // gives us a way to repeatedly request new tokens, so we utilize that and refresh the token + // every 2 minutes (less than half of the lifetime of the token). + let netrc_path_clone = path.to_path_buf(); + let initial_github_jwt_clone = flakehub_password.clone(); + let flakehub_cache_server_clone = flakehub_cache_server.to_string(); + let api_clone = api.clone(); + + tokio::task::spawn(refresh_github_actions_jwt_worker( + netrc_path_clone, + initial_github_jwt_clone, + flakehub_cache_server_clone, + api_clone, + )); + } } // Get the cache UUID for this project. diff --git a/magic-nix-cache/src/main.rs b/magic-nix-cache/src/main.rs index 515c829..d7848b1 100644 --- a/magic-nix-cache/src/main.rs +++ b/magic-nix-cache/src/main.rs @@ -23,7 +23,7 @@ mod telemetry; mod util; use std::collections::HashSet; -use std::fs::{self, create_dir_all}; +use std::fs::create_dir_all; use std::io::Write; use std::net::SocketAddr; use std::path::{Path, PathBuf}; @@ -59,13 +59,6 @@ type State = Arc; /// GitHub Actions-powered Nix binary cache #[derive(Parser, Debug)] struct Args { - /// JSON file containing credentials. - /// - /// If this is not specified, credentials will be loaded - /// from the environment. - #[arg(short = 'c', long)] - credentials_file: Option, - /// Address to listen on. /// /// FIXME: IPv6 @@ -193,6 +186,26 @@ struct StateInner { original_paths: Option>>, } +#[derive(Debug, Clone)] +pub(crate) enum FlakeHubAuthSource { + DeterminateNixd, + Netrc(PathBuf), +} + +impl FlakeHubAuthSource { + pub(crate) fn as_path_buf(&self) -> PathBuf { + match &self { + Self::Netrc(path) => path.clone(), + Self::DeterminateNixd => { + let mut path = PathBuf::from(DETERMINATE_STATE_DIR); + path.push("netrc"); + + path + } + } + } +} + async fn main_cli() -> Result<()> { let guard = init_logging()?; let _tracing_guard = guard.appender_guard; @@ -231,20 +244,23 @@ async fn main_cli() -> Result<()> { let narinfo_negative_cache = Arc::new(RwLock::new(HashSet::new())); + let auth_method: FlakeHubAuthSource = match (args.flakehub_api_server_netrc, dnixd_available) { + (None, true) => FlakeHubAuthSource::DeterminateNixd, + (Some(_), true) => { + tracing::info!("Ignoring the user-specified --flakehub-api-server-netrc, in favor of the determinate-nixd netrc"); + FlakeHubAuthSource::DeterminateNixd + } + (Some(path), false) => FlakeHubAuthSource::Netrc(path), + (None, false) => { + return Err(anyhow!( + "--flakehub-api-server-netrc is required when determinate-nixd is unavailable" + )); + } + }; + let flakehub_state = if args.use_flakehub { let flakehub_cache_server = args.flakehub_cache_server; - let flakehub_api_server_netrc = if dnixd_available { - let dnixd_netrc_path = PathBuf::from(DETERMINATE_STATE_DIR).join("netrc"); - args.flakehub_api_server_netrc.unwrap_or(dnixd_netrc_path) - } else { - args.flakehub_api_server_netrc.ok_or_else(|| { - anyhow!( - "--flakehub-api-server-netrc is required when determinate-nixd is unavailable" - ) - })? - }; - let flakehub_api_server = &args.flakehub_api_server; let flakehub_flake_name = args.flakehub_flake_name; @@ -252,22 +268,21 @@ async fn main_cli() -> Result<()> { match flakehub::init_cache( environment, flakehub_api_server, - &flakehub_api_server_netrc, &flakehub_cache_server, flakehub_flake_name, store.clone(), - dnixd_available, + &auth_method, ) .await { Ok(state) => { - if !dnixd_available { + if let FlakeHubAuthSource::Netrc(ref path) = auth_method { nix_conf .write_all( format!( "extra-substituters = {}?trusted=1\nnetrc-file = {}\n", &flakehub_cache_server, - flakehub_api_server_netrc.display() + path.display() ) .as_bytes(), ) @@ -290,26 +305,10 @@ async fn main_cli() -> Result<()> { }; let gha_cache = if args.use_gha_cache { - let credentials = if let Some(credentials_file) = &args.credentials_file { - tracing::info!("Loading credentials from {:?}", credentials_file); - let bytes = fs::read(credentials_file).with_context(|| { - format!( - "Failed to read credentials file '{}'", - credentials_file.display() - ) - })?; + tracing::info!("Loading credentials from environment"); - serde_json::from_slice(&bytes).with_context(|| { - format!( - "Failed to deserialize credentials file '{}'", - credentials_file.display() - ) - })? - } else { - tracing::info!("Loading credentials from environment"); - Credentials::load_from_env() - .with_context(|| "Failed to load credentials from environment (see README.md)")? - }; + let credentials = Credentials::load_from_env() + .with_context(|| "Failed to load credentials from environment (see README.md)")?; let gha_cache = gha::GhaCache::new( credentials,