From 765aa892be552ff4714787b6070599bf5e78e652 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Tue, 7 Sep 2021 17:10:11 +1000 Subject: [PATCH] linkd: Accept profile id and rad home from args Signed-off-by: Alexander Simmerl --- linkd/src/args.rs | 33 ++++++++---- linkd/src/cfg.rs | 22 ++++++-- test/examples/socket_activation_wrapper.rs | 2 +- .../linkd/socket_activation/unix.rs | 1 - test/src/test/unit/linkd/args.rs | 51 +++++++++++++++++++ 5 files changed, 95 insertions(+), 14 deletions(-) diff --git a/linkd/src/args.rs b/linkd/src/args.rs index 347fa6443..9ab55e846 100644 --- a/linkd/src/args.rs +++ b/linkd/src/args.rs @@ -3,12 +3,22 @@ // This file is part of radicle-link, distributed under the GPLv3 with Radicle // Linking Exception. For full terms see the included LICENSE file. -use std::{fmt::Display, path::PathBuf, str::FromStr}; +use std::{fmt, path::PathBuf, str::FromStr}; use structopt::StructOpt; +use librad::profile::{ProfileId, RadHome}; + #[derive(Debug, Default, Eq, PartialEq, StructOpt)] pub struct Args { + #[structopt(long)] + pub profile_id: Option, + + /// Home of the profile data, if not provided is read from the environment + /// and falls back to project dirs. + #[structopt(long, default_value, parse(from_str = parse_rad_home))] + pub rad_home: RadHome, + /// Configures the type of signer used to get access to the storage. #[structopt(long, default_value)] pub signer: Signer, @@ -21,16 +31,21 @@ pub struct Args { required_if("key-source", "file") )] pub key_file_path: PathBuf, - /// Format of the key input data. #[structopt(long, default_value, required_if("signer", "key"))] pub key_format: KeyFormat, - /// Specifies from which source the secret should be read. #[structopt(long, default_value, required_if("signer", "key"))] pub key_source: KeySource, } +fn parse_rad_home(src: &str) -> RadHome { + match src { + dirs if dirs == RadHome::ProjectDirs.to_string() => RadHome::ProjectDirs, + _ => RadHome::Root(PathBuf::from(src)), + } +} + #[derive(Debug, Eq, PartialEq, StructOpt)] pub enum Signer { /// Construct signer from a secret key. @@ -45,8 +60,8 @@ impl Default for Signer { } } -impl Display for Signer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for Signer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let ty = match self { Self::Key => "key", Self::SshAgent => "ssh-agent", @@ -80,8 +95,8 @@ impl Default for KeyFormat { } } -impl Display for KeyFormat { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for KeyFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let source = match self { Self::Base64 => "base64", Self::Binary => "binary", @@ -114,8 +129,8 @@ impl Default for KeySource { } } -impl Display for KeySource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for KeySource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { let source = match self { Self::File => "file", Self::Stdin => "stdin", diff --git a/linkd/src/cfg.rs b/linkd/src/cfg.rs index 2a005adba..eebc746f8 100644 --- a/linkd/src/cfg.rs +++ b/linkd/src/cfg.rs @@ -4,7 +4,8 @@ // Linking Exception. For full terms see the included LICENSE file. use std::{ - io::{self}, + convert::TryFrom, + io, net::{Ipv4Addr, SocketAddr, SocketAddrV4}, time::Duration, }; @@ -19,6 +20,7 @@ use tokio::{ use librad::{ crypto::{BoxedSigner, IntoSecretKeyError}, + git::storage, keystore::SecretKeyExt as _, net, net::peer::Config as PeerConfig, @@ -28,7 +30,7 @@ use librad::{ }; use link_clib::keys; -use crate::args::{self}; +use crate::args; lazy_static::lazy_static! { /// Localhost binding to any available port, i.e. `127.0.0.1:0`. @@ -44,6 +46,9 @@ pub enum Error { #[error(transparent)] IO(#[from] io::Error), + #[error(transparent)] + Init(#[from] storage::error::Init), + #[error(transparent)] Keys(#[from] keys::Error), @@ -67,9 +72,12 @@ impl Cfg { where S: ClientStream + Unpin + 'static, { - let profile = Profile::from_root(std::path::Path::new("/tmp/linkd"), None)?; + let profile = Profile::try_from(&args)?; let signer = construct_signer::(args, &profile).await?; + // Ensure the storage is accessible for the created profile and signer. + storage::Storage::init(profile.paths(), signer.clone())?; + Ok(Self { bootstrap: vec![], peer: PeerConfig { @@ -90,6 +98,14 @@ impl Cfg { } } +impl TryFrom<&args::Args> for Profile { + type Error = Error; + + fn try_from(args: &args::Args) -> Result { + Profile::from_home(&args.rad_home, args.profile_id.clone()).map_err(Error::from) + } +} + async fn construct_signer(args: args::Args, profile: &Profile) -> Result where S: ClientStream + Unpin + 'static, diff --git a/test/examples/socket_activation_wrapper.rs b/test/examples/socket_activation_wrapper.rs index fae835cbd..28fd27b45 100644 --- a/test/examples/socket_activation_wrapper.rs +++ b/test/examples/socket_activation_wrapper.rs @@ -8,7 +8,7 @@ use nix::{sys::socket, unistd::Pid}; use std::{fs::remove_file, os::unix::process::CommandExt as _, process::Command}; fn main() -> Result<()> { - remove_file("/tmp/test-linkd-socket-activation.sock")?; + remove_file("/tmp/test-linkd-socket-activation.sock").ok(); let sock = socket::socket( socket::AddressFamily::Unix, diff --git a/test/src/test/integration/linkd/socket_activation/unix.rs b/test/src/test/integration/linkd/socket_activation/unix.rs index 902502523..f51d8483b 100644 --- a/test/src/test/integration/linkd/socket_activation/unix.rs +++ b/test/src/test/integration/linkd/socket_activation/unix.rs @@ -6,7 +6,6 @@ use std::process::Command; use anyhow::Result; -use assert_cmd; #[test] fn construct_listener_from_env() -> Result<()> { diff --git a/test/src/test/unit/linkd/args.rs b/test/src/test/unit/linkd/args.rs index 4f471d9e1..d71d71f9f 100644 --- a/test/src/test/unit/linkd/args.rs +++ b/test/src/test/unit/linkd/args.rs @@ -8,6 +8,8 @@ use std::path::PathBuf; use anyhow::Result; use structopt::StructOpt as _; +use librad::profile::{ProfileId, RadHome}; + use linkd::args::{self, Args}; #[test] @@ -15,9 +17,58 @@ fn defaults() -> Result<()> { let iter = vec!["linkd"]; let parsed = Args::from_iter_safe(iter)?; + assert_matches!( + parsed, + Args { + rad_home: RadHome::ProjectDirs, + .. + } + ); + assert_eq!( + parsed, + Args { + ..Default::default() + } + ); + + Ok(()) +} + +#[test] +fn profile_id() -> Result<()> { + let id = ProfileId::new(); + + #[rustfmt::skip] + let iter = vec![ + "linkd", + "--profile-id", id.as_str() + ]; + let parsed = Args::from_iter_safe(iter)?; + + assert_eq!( + parsed, + Args { + profile_id: Some(id), + ..Default::default() + } + ); + + Ok(()) +} + +#[test] +fn rad_home() -> Result<()> { + #[rustfmt::skip] + let iter = vec![ + "linkd", + "--rad-home", "/tmp/linkd", + ]; + let parsed = Args::from_iter_safe(iter)?; + assert_eq!( parsed, Args { + rad_home: RadHome::Root(PathBuf::from("/tmp/linkd")), ..Default::default() } );