Skip to content
This repository has been archived by the owner on Oct 23, 2022. It is now read-only.

Commit

Permalink
Merge #423
Browse files Browse the repository at this point in the history
423: http: configuration refactor r=niklaslong a=niklaslong

This is a wip but opening as a draft to keep track of this follow up to #421. This is also part of #402. 

This introduces the `Config` struct to serve as a facade to the configuration module. It's essentially a flattened `CompatibleConfigFile` struct exposing only the fields necessary to running the daemon.  


Co-authored-by: Niklas Long <[email protected]>
  • Loading branch information
bors[bot] and niklaslong authored Oct 30, 2020
2 parents 83e73ab + eb005bb commit 73409a8
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 58 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Next

* refactor(http): introduce `Config` as the facade for configuration [#423]
* feat(http): create `Profile` abstraction [#421]

[#423]: https://github.com/rs-ipfs/rust-ipfs/pull/423
[#421]: https://github.com/rs-ipfs/rust-ipfs/pull/421

# 0.2.1
Expand Down
106 changes: 61 additions & 45 deletions http/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod keys_proto {
include!(concat!(env!("OUT_DIR"), "/keys_proto.rs"));
}

/// Defines the configuration types supported by the API.
#[derive(Debug, StructOpt)]
pub enum Profile {
Test,
Expand All @@ -34,7 +35,7 @@ impl FromStr for Profile {
}
}

/// The way things can go wrong when calling [`initialize`].
/// The way things can go wrong when calling [`init`].
#[derive(Error, Debug)]
pub enum InitializationError {
#[error("repository creation failed: {0}")]
Expand All @@ -50,50 +51,29 @@ pub enum InitializationError {
#[error("key encoding failed: {0}")]
PrivateKeyEncodingFailed(prost::EncodeError),
#[error("config serialization failed: {0}")]
ConfigWritingFailed(serde_json::Error),
ConfigWritingFailed(Box<dyn std::error::Error + 'static>),
}

/// Creates the IPFS_PATH directory structure and creates a new compatible configuration file with
/// RSA key of length `bits`.
pub fn initialize(
/// RSA key of length `bits`. Returns the Peer ID.
pub fn init(
ipfs_path: &Path,
bits: NonZeroU16,
profiles: Vec<Profile>,
) -> Result<(), InitializationError> {
// This check is done here to avoid an empty config file being created in the case of an
// unsupported input.
if profiles.len() != 1 {
unimplemented!("Multiple profiles are currently unsupported!");
}

let config_path = ipfs_path.join("config");

fs::create_dir_all(&ipfs_path)
.map_err(InitializationError::DirectoryCreationFailed)
.and_then(|_| {
fs::File::create(&config_path).map_err(InitializationError::ConfigCreationFailed)
})
.and_then(|config_file| create(config_file, bits, profiles))
}

fn create(
config: File,
bits: NonZeroU16,
profiles: Vec<Profile>,
) -> Result<(), InitializationError> {
) -> Result<String, InitializationError> {
use multibase::Base::Base64Pad;
use prost::Message;
use std::io::BufWriter;
use std::fs::OpenOptions;
use std::io::{BufWriter, Write};

let api_addr = match profiles[0] {
Profile::Test => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(0u16)),
Profile::Default => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(4004u16)),
};
if profiles.len() != 1 {
unimplemented!("Multiple profiles are currently unsupported!")
}

let bits = bits.get();

if bits < 2048 || bits > 16 * 1024 {
// ring will not accept a less than 2048 key
// Ring won't accept less than a 2048 bit key.
return Err(InitializationError::InvalidRsaKeyLength(bits));
}

Expand Down Expand Up @@ -135,9 +115,14 @@ fn create(

let private_key = Base64Pad.encode(&private_key);

let api_addr = match profiles[0] {
Profile::Test => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(0u16)),
Profile::Default => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(4004u16)),
};

let config_contents = CompatibleConfigFile {
identity: Identity {
peer_id,
peer_id: peer_id.clone(),
private_key,
},
addresses: Addresses {
Expand All @@ -146,10 +131,38 @@ fn create(
},
};

serde_json::to_writer_pretty(BufWriter::new(config), &config_contents)
.map_err(InitializationError::ConfigWritingFailed)?;
let config_path = ipfs_path.join("config");

Ok(())
let config_file = fs::create_dir_all(&ipfs_path)
.map_err(InitializationError::DirectoryCreationFailed)
.and_then(|_| {
OpenOptions::new()
.write(true)
.create_new(true)
.open(&config_path)
.map_err(InitializationError::ConfigCreationFailed)
})?;

let mut writer = BufWriter::new(config_file);

serde_json::to_writer_pretty(&mut writer, &config_contents)
.map_err(|e| InitializationError::ConfigWritingFailed(Box::new(e)))?;

writer
.flush()
.map_err(|e| InitializationError::ConfigWritingFailed(Box::new(e)))?;

Ok(peer_id)
}

/// The facade for the configuration of the API.
pub struct Config {
/// Keypair for the ipfs node.
pub keypair: ipfs::Keypair,
/// Peer addresses for the ipfs node.
pub swarm: Vec<Multiaddr>,
/// Address to run the API daemon on.
pub api_addr: Multiaddr,
}

/// Things which can go wrong when loading a `go-ipfs` compatible configuration file.
Expand All @@ -172,27 +185,30 @@ pub enum LoadingError {
/// Returns only the keypair and listening addresses or [`LoadingError`] but this should be
/// extended to contain the bootstrap nodes at least later when we need to support those for
/// testing purposes.
pub fn load(config: File) -> Result<(ipfs::Keypair, Vec<Multiaddr>, Multiaddr), LoadingError> {
pub fn load(config: File) -> Result<Config, LoadingError> {
use std::io::BufReader;

let CompatibleConfigFile {
identity,
addresses,
} = serde_json::from_reader(BufReader::new(config))
let config_file: CompatibleConfigFile = serde_json::from_reader(BufReader::new(config))
.map_err(LoadingError::ConfigurationFileFormat)?;

let kp = identity.load_keypair()?;
let kp = config_file.identity.load_keypair()?;

let peer_id = kp.public().into_peer_id().to_string();

if peer_id != identity.peer_id {
if peer_id != config_file.identity.peer_id {
return Err(LoadingError::PeerIdMismatch {
loaded: peer_id,
stored: identity.peer_id,
stored: config_file.identity.peer_id,
});
}

Ok((kp, addresses.swarm, addresses.api))
let config = Config {
keypair: kp,
swarm: config_file.addresses.swarm,
api_addr: config_file.addresses.api,
};

Ok(config)
}

/// Converts a PEM format to DER where PEM is a container for Base64 data with padding, starting on
Expand Down
22 changes: 9 additions & 13 deletions http/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn main() {

let config_path = home.join("config");

let (keypair, listening_addrs, api_listening_addr) = match opts {
let config = match opts {
Options::Init { bits, profile } => {
println!("initializing IPFS node at {:?}", home);

Expand All @@ -73,20 +73,14 @@ fn main() {
std::process::exit(1);
}

let result = config::initialize(&home, bits, profile);
let result = config::init(&home, bits, profile);

match result {
Ok(_) => {
let (kp, _, _) = std::fs::File::open(config_path)
.map_err(config::LoadingError::ConfigurationFileOpening)
.and_then(config::load)
.unwrap();

Ok(peer_id) => {
// go-ipfs prints here (in addition to earlier "initializing ..."):
//
// generating {}-bit RSA keypair...done

println!("peer identity: {}", kp.public().into_peer_id());
println!("peer identity: {}", peer_id);
std::process::exit(0);
}
Err(config::InitializationError::DirectoryCreationFailed(e)) => {
Expand Down Expand Up @@ -141,11 +135,11 @@ fn main() {
rt.block_on(async move {
let opts = IpfsOptions {
ipfs_path: home.clone(),
keypair,
keypair: config.keypair,
bootstrap: Vec::new(),
mdns: false,
kad_protocol: None,
listening_addrs,
listening_addrs: config.swarm,
span: None,
};

Expand All @@ -158,11 +152,13 @@ fn main() {

let api_link_file = home.join("api");

let (addr, server) = serve(&ipfs, api_listening_addr);
let (addr, server) = serve(&ipfs, config.api_addr);

// shutdown future will handle signalling the exit
drop(ipfs);

// We can't simply reuse the address from the config as the test profile uses ephemeral
// ports.
let api_multiaddr = format!("/ip4/{}/tcp/{}", addr.ip(), addr.port());

// this file is looked for when js-ipfsd-ctl checks optimistically if the IPFS_PATH has a
Expand Down

0 comments on commit 73409a8

Please sign in to comment.