forked from lix-project/lix-installer
Explicit proxy support (#337)
* Add proxy support * Improve clap definition and check * Include missing protocol * Improve error
This commit is contained in:
parent
a977370e74
commit
c13b08987b
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -1313,6 +1313,7 @@ dependencies = [
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
|
"tokio-socks",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
|
@ -1823,6 +1824,18 @@ dependencies = [
|
||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-socks"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"futures-util",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.7"
|
version = "0.7.7"
|
||||||
|
|
|
@ -33,7 +33,7 @@ eyre = { version = "0.6.8", default-features = false, features = [ "track-caller
|
||||||
glob = { version = "0.3.0", default-features = false }
|
glob = { version = "0.3.0", default-features = false }
|
||||||
nix = { version = "0.26.0", default-features = false, features = ["user", "fs", "process", "term"] }
|
nix = { version = "0.26.0", default-features = false, features = ["user", "fs", "process", "term"] }
|
||||||
owo-colors = { version = "3.5.0", default-features = false, features = [ "supports-colors" ] }
|
owo-colors = { version = "3.5.0", default-features = false, features = [ "supports-colors" ] }
|
||||||
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls-native-roots", "stream"] }
|
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls-native-roots", "stream", "socks"] }
|
||||||
serde = { version = "1.0.144", default-features = false, features = [ "std", "derive" ] }
|
serde = { version = "1.0.144", default-features = false, features = [ "std", "derive" ] }
|
||||||
serde_json = { version = "1.0.85", default-features = false, features = [ "std" ] }
|
serde_json = { version = "1.0.85", default-features = false, features = [ "std" ] }
|
||||||
serde_with = { version = "2.0.1", default-features = false, features = [ "std", "macros" ] }
|
serde_with = { version = "2.0.1", default-features = false, features = [ "std", "macros" ] }
|
||||||
|
|
|
@ -13,11 +13,16 @@ Fetch a URL to the given path
|
||||||
pub struct FetchAndUnpackNix {
|
pub struct FetchAndUnpackNix {
|
||||||
url: Url,
|
url: Url,
|
||||||
dest: PathBuf,
|
dest: PathBuf,
|
||||||
|
proxy: Option<Url>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchAndUnpackNix {
|
impl FetchAndUnpackNix {
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub async fn plan(url: Url, dest: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
pub async fn plan(
|
||||||
|
url: Url,
|
||||||
|
dest: PathBuf,
|
||||||
|
proxy: Option<Url>,
|
||||||
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
// TODO(@hoverbear): Check URL exists?
|
// TODO(@hoverbear): Check URL exists?
|
||||||
// TODO(@hoverbear): Check tempdir exists
|
// TODO(@hoverbear): Check tempdir exists
|
||||||
|
|
||||||
|
@ -30,7 +35,18 @@ impl FetchAndUnpackNix {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self { url, dest }.into())
|
if let Some(proxy) = &proxy {
|
||||||
|
match proxy.scheme() {
|
||||||
|
"https" | "http" | "socks5" => (),
|
||||||
|
_ => {
|
||||||
|
return Err(ActionError::Custom(Box::new(
|
||||||
|
FetchUrlError::UnknownProxyScheme,
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { url, dest, proxy }.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,12 +61,17 @@ impl Action for FetchAndUnpackNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tracing_span(&self) -> Span {
|
fn tracing_span(&self) -> Span {
|
||||||
span!(
|
let span = span!(
|
||||||
tracing::Level::DEBUG,
|
tracing::Level::DEBUG,
|
||||||
"fetch_and_unpack_nix",
|
"fetch_and_unpack_nix",
|
||||||
url = tracing::field::display(&self.url),
|
url = tracing::field::display(&self.url),
|
||||||
|
proxy = tracing::field::Empty,
|
||||||
dest = tracing::field::display(self.dest.display()),
|
dest = tracing::field::display(self.dest.display()),
|
||||||
)
|
);
|
||||||
|
if let Some(proxy) = &self.proxy {
|
||||||
|
span.record("proxy", tracing::field::display(&proxy));
|
||||||
|
}
|
||||||
|
span
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_description(&self) -> Vec<ActionDescription> {
|
fn execute_description(&self) -> Vec<ActionDescription> {
|
||||||
|
@ -59,11 +80,24 @@ impl Action for FetchAndUnpackNix {
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { url, dest } = self;
|
let bytes = match self.url.scheme() {
|
||||||
|
|
||||||
let bytes = match url.scheme() {
|
|
||||||
"https" | "http" => {
|
"https" | "http" => {
|
||||||
let res = reqwest::get(url.clone())
|
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(|e| {
|
||||||
|
ActionError::Custom(Box::new(FetchUrlError::Reqwest(e)))
|
||||||
|
})?)
|
||||||
|
}
|
||||||
|
let client = buildable_client
|
||||||
|
.build()
|
||||||
|
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
||||||
|
let req = client
|
||||||
|
.get(self.url.clone())
|
||||||
|
.build()
|
||||||
|
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
||||||
|
let res = client
|
||||||
|
.execute(req)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
||||||
res.bytes()
|
res.bytes()
|
||||||
|
@ -71,9 +105,9 @@ impl Action for FetchAndUnpackNix {
|
||||||
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?
|
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?
|
||||||
},
|
},
|
||||||
"file" => {
|
"file" => {
|
||||||
let buf = tokio::fs::read(url.path())
|
let buf = tokio::fs::read(self.url.path())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Read(PathBuf::from(url.path()), e))?;
|
.map_err(|e| ActionError::Read(PathBuf::from(self.url.path()), e))?;
|
||||||
Bytes::from(buf)
|
Bytes::from(buf)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -85,7 +119,7 @@ impl Action for FetchAndUnpackNix {
|
||||||
|
|
||||||
// TODO(@Hoverbear): Pick directory
|
// TODO(@Hoverbear): Pick directory
|
||||||
tracing::trace!("Unpacking tar.xz");
|
tracing::trace!("Unpacking tar.xz");
|
||||||
let dest_clone = dest.clone();
|
let dest_clone = self.dest.clone();
|
||||||
|
|
||||||
let decoder = xz2::read::XzDecoder::new(bytes.reader());
|
let decoder = xz2::read::XzDecoder::new(bytes.reader());
|
||||||
let mut archive = tar::Archive::new(decoder);
|
let mut archive = tar::Archive::new(decoder);
|
||||||
|
@ -102,8 +136,6 @@ impl Action for FetchAndUnpackNix {
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { url: _, dest: _ } = self;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +151,8 @@ pub enum FetchUrlError {
|
||||||
),
|
),
|
||||||
#[error("Unarchiving error")]
|
#[error("Unarchiving error")]
|
||||||
Unarchive(#[source] std::io::Error),
|
Unarchive(#[source] std::io::Error),
|
||||||
#[error("Unknown url scheme")]
|
#[error("Unknown url scheme, `file://`, `https://` and `http://` supported")]
|
||||||
UnknownUrlScheme,
|
UnknownUrlScheme,
|
||||||
|
#[error("Unknown url scheme, `https://`, `socks5://`, and `http://` supported")]
|
||||||
|
UnknownProxyScheme,
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,12 @@ pub struct ProvisionNix {
|
||||||
impl ProvisionNix {
|
impl ProvisionNix {
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let fetch_nix =
|
let fetch_nix = FetchAndUnpackNix::plan(
|
||||||
FetchAndUnpackNix::plan(settings.nix_package_url.clone(), PathBuf::from(SCRATCH_DIR))
|
settings.nix_package_url.clone(),
|
||||||
.await?;
|
PathBuf::from(SCRATCH_DIR),
|
||||||
|
settings.proxy.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
|
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::Child(CreateUsersAndGroups::action_tag(), Box::new(e)))?;
|
.map_err(|e| ActionError::Child(CreateUsersAndGroups::action_tag(), Box::new(e)))?;
|
||||||
|
|
|
@ -172,6 +172,10 @@ pub struct CommonSettings {
|
||||||
)]
|
)]
|
||||||
pub(crate) nix_package_url: Url,
|
pub(crate) nix_package_url: Url,
|
||||||
|
|
||||||
|
/// The proxy to use (if any), valid proxy bases are `https://$URL`, `http://$URL` and `socks5://$URL`
|
||||||
|
#[cfg_attr(feature = "cli", clap(long, env = "NIX_INSTALLER_PROXY"))]
|
||||||
|
pub(crate) proxy: Option<Url>,
|
||||||
|
|
||||||
/// Extra configuration lines for `/etc/nix.conf`
|
/// Extra configuration lines for `/etc/nix.conf`
|
||||||
#[cfg_attr(feature = "cli", clap(long, action = ArgAction::Set, num_args = 0.., value_delimiter = ',', env = "NIX_INSTALLER_EXTRA_CONF", global = true))]
|
#[cfg_attr(feature = "cli", clap(long, action = ArgAction::Set, num_args = 0.., value_delimiter = ',', env = "NIX_INSTALLER_EXTRA_CONF", global = true))]
|
||||||
pub extra_conf: Vec<String>,
|
pub extra_conf: Vec<String>,
|
||||||
|
@ -272,6 +276,7 @@ impl CommonSettings {
|
||||||
nix_build_user_prefix: nix_build_user_prefix.to_string(),
|
nix_build_user_prefix: nix_build_user_prefix.to_string(),
|
||||||
nix_build_user_id_base,
|
nix_build_user_id_base,
|
||||||
nix_package_url: url.parse()?,
|
nix_package_url: url.parse()?,
|
||||||
|
proxy: Default::default(),
|
||||||
extra_conf: Default::default(),
|
extra_conf: Default::default(),
|
||||||
force: false,
|
force: false,
|
||||||
#[cfg(feature = "diagnostics")]
|
#[cfg(feature = "diagnostics")]
|
||||||
|
@ -291,6 +296,7 @@ impl CommonSettings {
|
||||||
nix_build_user_prefix,
|
nix_build_user_prefix,
|
||||||
nix_build_user_id_base,
|
nix_build_user_id_base,
|
||||||
nix_package_url,
|
nix_package_url,
|
||||||
|
proxy,
|
||||||
extra_conf,
|
extra_conf,
|
||||||
force,
|
force,
|
||||||
#[cfg(feature = "diagnostics")]
|
#[cfg(feature = "diagnostics")]
|
||||||
|
@ -326,6 +332,7 @@ impl CommonSettings {
|
||||||
"nix_package_url".into(),
|
"nix_package_url".into(),
|
||||||
serde_json::to_value(nix_package_url)?,
|
serde_json::to_value(nix_package_url)?,
|
||||||
);
|
);
|
||||||
|
map.insert("proxy".into(), serde_json::to_value(proxy)?);
|
||||||
map.insert("extra_conf".into(), serde_json::to_value(extra_conf)?);
|
map.insert("extra_conf".into(), serde_json::to_value(extra_conf)?);
|
||||||
map.insert("force".into(), serde_json::to_value(force)?);
|
map.insert("force".into(), serde_json::to_value(force)?);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue