Skip to content

Commit

Permalink
Merge pull request #103 from DeterminateSystems/fh-cache-under-determ…
Browse files Browse the repository at this point in the history
…inate

Drop the assertion around the netrc under dnixd
  • Loading branch information
grahamc committed Sep 17, 2024
2 parents 5b126b6 + 04af540 commit 7c6bd93
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 81 deletions.
68 changes: 29 additions & 39 deletions magic-nix-cache/src/flakehub.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<String>,
store: Arc<NixStore>,
using_dnixd: bool,
auth_method: &super::FlakeHubAuthSource,
) -> Result<State> {
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
Expand All @@ -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
))
})?;
Expand Down Expand Up @@ -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
Expand All @@ -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
))
})?;
Expand All @@ -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
))
})?;
Expand All @@ -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.
Expand Down
83 changes: 41 additions & 42 deletions magic-nix-cache/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -59,13 +59,6 @@ type State = Arc<StateInner>;
/// 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<PathBuf>,

/// Address to listen on.
///
/// FIXME: IPv6
Expand Down Expand Up @@ -193,6 +186,26 @@ struct StateInner {
original_paths: Option<Mutex<HashSet<PathBuf>>>,
}

#[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;
Expand Down Expand Up @@ -231,43 +244,45 @@ 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;

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(),
)
Expand All @@ -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,
Expand Down

0 comments on commit 7c6bd93

Please sign in to comment.