Skip to content

Commit

Permalink
Add support for URLs or paths in --nix-package-url and --extra-conf (#…
Browse files Browse the repository at this point in the history
…634)

* Add support for URLs or paths in --nix-package-url and --extra-conf

* fmt

* Into a mod with you, tests!
  • Loading branch information
Hoverbear authored Sep 20, 2023
1 parent 60e5fff commit abfde74
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 77 deletions.
114 changes: 61 additions & 53 deletions src/action/base/fetch_and_unpack_nix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ use tracing::{span, Span};
use crate::{
action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction},
parse_ssl_cert,
settings::UrlOrPath,
};

/**
Fetch a URL to the given path
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct FetchAndUnpackNix {
url: Url,
url_or_path: UrlOrPath,
dest: PathBuf,
proxy: Option<Url>,
ssl_cert_file: Option<PathBuf>,
Expand All @@ -23,18 +24,20 @@ pub struct FetchAndUnpackNix {
impl FetchAndUnpackNix {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(
url: Url,
url_or_path: UrlOrPath,
dest: PathBuf,
proxy: Option<Url>,
ssl_cert_file: Option<PathBuf>,
) -> Result<StatefulAction<Self>, ActionError> {
// TODO(@hoverbear): Check URL exists?
// TODO(@hoverbear): Check tempdir exists

match url.scheme() {
"https" | "http" | "file" => (),
_ => return Err(Self::error(FetchUrlError::UnknownUrlScheme)),
};
if let UrlOrPath::Url(url) = &url_or_path {
match url.scheme() {
"https" | "http" | "file" => (),
_ => return Err(Self::error(ActionErrorKind::UnknownUrlScheme)),
}
}

if let Some(proxy) = &proxy {
match proxy.scheme() {
Expand All @@ -48,7 +51,7 @@ impl FetchAndUnpackNix {
}

Ok(Self {
url,
url_or_path,
dest,
proxy,
ssl_cert_file,
Expand All @@ -64,14 +67,14 @@ impl Action for FetchAndUnpackNix {
ActionTag("fetch_and_unpack_nix")
}
fn tracing_synopsis(&self) -> String {
format!("Fetch `{}` to `{}`", self.url, self.dest.display())
format!("Fetch `{}` to `{}`", self.url_or_path, self.dest.display())
}

fn tracing_span(&self) -> Span {
let span = span!(
tracing::Level::DEBUG,
"fetch_and_unpack_nix",
url = tracing::field::display(&self.url),
url_or_path = tracing::field::display(&self.url_or_path),
proxy = tracing::field::Empty,
ssl_cert_file = tracing::field::Empty,
dest = tracing::field::display(self.dest.display()),
Expand All @@ -94,47 +97,60 @@ impl Action for FetchAndUnpackNix {

#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
let bytes = match self.url.scheme() {
"https" | "http" => {
let mut buildable_client = reqwest::Client::builder();
if let Some(proxy) = &self.proxy {
buildable_client = buildable_client.proxy(
reqwest::Proxy::all(proxy.clone())
.map_err(FetchUrlError::Reqwest)
.map_err(Self::error)?,
)
}
if let Some(ssl_cert_file) = &self.ssl_cert_file {
let ssl_cert = parse_ssl_cert(ssl_cert_file).await.map_err(Self::error)?;
buildable_client = buildable_client.add_root_certificate(ssl_cert);
}
let client = buildable_client
.build()
.map_err(FetchUrlError::Reqwest)
.map_err(Self::error)?;
let req = client
.get(self.url.clone())
.build()
.map_err(FetchUrlError::Reqwest)
.map_err(Self::error)?;
let res = client
.execute(req)
.await
.map_err(FetchUrlError::Reqwest)
.map_err(Self::error)?;
res.bytes()
.await
.map_err(FetchUrlError::Reqwest)
.map_err(Self::error)?
let bytes = match &self.url_or_path {
UrlOrPath::Url(url) => {
let bytes = match url.scheme() {
"https" | "http" => {
let mut buildable_client = reqwest::Client::builder();
if let Some(proxy) = &self.proxy {
buildable_client = buildable_client.proxy(
reqwest::Proxy::all(proxy.clone())
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?,
)
}
if let Some(ssl_cert_file) = &self.ssl_cert_file {
let ssl_cert =
parse_ssl_cert(ssl_cert_file).await.map_err(Self::error)?;
buildable_client = buildable_client.add_root_certificate(ssl_cert);
}
let client = buildable_client
.build()
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?;
let req = client
.get(url.clone())
.build()
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?;
let res = client
.execute(req)
.await
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?;
res.bytes()
.await
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?
},
"file" => {
let buf = tokio::fs::read(url.path())
.await
.map_err(|e| ActionErrorKind::Read(PathBuf::from(url.path()), e))
.map_err(Self::error)?;
Bytes::from(buf)
},
_ => return Err(Self::error(ActionErrorKind::UnknownUrlScheme)),
};
bytes
},
"file" => {
let buf = tokio::fs::read(self.url.path())
UrlOrPath::Path(path) => {
let buf = tokio::fs::read(path)
.await
.map_err(|e| ActionErrorKind::Read(PathBuf::from(self.url.path()), e))
.map_err(|e| ActionErrorKind::Read(PathBuf::from(path), e))
.map_err(Self::error)?;
Bytes::from(buf)
},
_ => return Err(Self::error(FetchUrlError::UnknownUrlScheme)),
};

// TODO(@Hoverbear): Pick directory
Expand Down Expand Up @@ -167,16 +183,8 @@ impl Action for FetchAndUnpackNix {
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum FetchUrlError {
#[error("Request error")]
Reqwest(
#[from]
#[source]
reqwest::Error,
),
#[error("Unarchiving error")]
Unarchive(#[source] std::io::Error),
#[error("Unknown url scheme, `file://`, `https://` and `http://` supported")]
UnknownUrlScheme,
#[error("Unknown proxy scheme, `https://`, `socks5://`, and `http://` supported")]
UnknownProxyScheme,
}
Expand Down
1 change: 1 addition & 0 deletions src/action/common/configure_nix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl ConfigureNix {
};
let place_nix_configuration = PlaceNixConfiguration::plan(
settings.nix_build_group_name.clone(),
settings.proxy.clone(),
settings.ssl_cert_file.clone(),
settings.extra_conf.clone(),
settings.force,
Expand Down
62 changes: 59 additions & 3 deletions src/action/common/place_nix_configuration.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use tracing::{span, Span};
use url::Url;

use crate::action::base::create_or_insert_into_file::Position;
use crate::action::base::{CreateDirectory, CreateFile, CreateOrInsertIntoFile};
use crate::action::{
Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction,
};
use crate::parse_ssl_cert;
use crate::settings::UrlOrPathOrString;
use std::path::{Path, PathBuf};

const NIX_CONF_FOLDER: &str = "/etc/nix";
Expand All @@ -24,17 +27,70 @@ impl PlaceNixConfiguration {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(
nix_build_group_name: String,
proxy: Option<Url>,
ssl_cert_file: Option<PathBuf>,
extra_conf: Vec<String>,
extra_conf: Vec<UrlOrPathOrString>,
force: bool,
) -> Result<StatefulAction<Self>, ActionError> {
let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force)
.await
.map_err(Self::error)?;

let mut extra_conf_text = vec![];
for extra in extra_conf {
let buf = match &extra {
UrlOrPathOrString::Url(url) => match url.scheme() {
"https" | "http" => {
let mut buildable_client = reqwest::Client::builder();
if let Some(proxy) = &proxy {
buildable_client = buildable_client.proxy(
reqwest::Proxy::all(proxy.clone())
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?,
)
}
if let Some(ssl_cert_file) = &ssl_cert_file {
let ssl_cert =
parse_ssl_cert(ssl_cert_file).await.map_err(Self::error)?;
buildable_client = buildable_client.add_root_certificate(ssl_cert);
}
let client = buildable_client
.build()
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?;
let req = client
.get(url.clone())
.build()
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?;
let res = client
.execute(req)
.await
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?;
res.text()
.await
.map_err(ActionErrorKind::Reqwest)
.map_err(Self::error)?
},
"file" => tokio::fs::read_to_string(url.path())
.await
.map_err(|e| ActionErrorKind::Read(PathBuf::from(url.path()), e))
.map_err(Self::error)?,
_ => return Err(Self::error(ActionErrorKind::UnknownUrlScheme)),
},
UrlOrPathOrString::Path(path) => tokio::fs::read_to_string(path)
.await
.map_err(|e| ActionErrorKind::Read(PathBuf::from(path), e))
.map_err(Self::error)?,
UrlOrPathOrString::String(string) => string.clone(),
};
extra_conf_text.push(buf)
}

let mut nix_conf_insert_settings = Vec::default();
nix_conf_insert_settings.push("include ./nix-installer-defaults.conf".into());
nix_conf_insert_settings.extend(extra_conf);
nix_conf_insert_settings.extend(extra_conf_text);
let nix_conf_insert_fragment = nix_conf_insert_settings.join("\n");

let mut defaults_conf_settings = vec![
Expand Down Expand Up @@ -95,7 +151,7 @@ impl PlaceNixConfiguration {

// We only scan one include of depth -- we should make this any depth, make sure to guard for loops
if line.starts_with("include") || line.starts_with("!include") {
let allow_not_existing = line.starts_with("!");
let allow_not_existing = line.starts_with('!');
// Need to read it in if it exists for settings
let path = line
.trim_start_matches("include")
Expand Down
12 changes: 11 additions & 1 deletion src/action/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ use std::{error::Error, process::Output};
use tokio::task::JoinError;
use tracing::Span;

use crate::{error::HasExpectedErrors, CertificateError};
use crate::{error::HasExpectedErrors, settings::UrlOrPathError, CertificateError};

use self::base::create_or_insert_into_file::Position;

Expand Down Expand Up @@ -585,6 +585,16 @@ pub enum ActionErrorKind {
SystemdMissing,
#[error("`{command}` failed, message: {message}")]
DiskUtilInfoError { command: String, message: String },
#[error(transparent)]
UrlOrPathError(#[from] UrlOrPathError),
#[error("Request error")]
Reqwest(
#[from]
#[source]
reqwest::Error,
),
#[error("Unknown url scheme")]
UnknownUrlScheme,
}

impl ActionErrorKind {
Expand Down
Loading

0 comments on commit abfde74

Please sign in to comment.