Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions brane-ctl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,26 @@ diesel_migrations = "2.2.0"
dirs = "5.0.1"
dotenvy = "0.15.0"
eflint-to-json = { git = "https://github.com/epi-project/policy-reasoner" }
enum-debug.workspace = true
error-trace.workspace = true
enum-debug = { workspace = true, features = ["derive"] }
error-trace = { workspace = true }
# env_logger = "0.10"
gethostname = "0.5.0"
hex-literal = "0.4.0"
humanlog.workspace = true
humantime = "2.1.0"
human-panic = "2.0.0"
humantime = "2.1.0"
jsonwebtoken = "9.2.0"
lazy_static = "1.4.0"
log = "0.4.22"
names.workspace = true
policy = { git = "https://github.com/epi-project/policy-reasoner" }
srv = { git = "https://github.com/epi-project/policy-reasoner" }
rand = "0.8.5"
reqwest = { version = "0.11.27" }
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120"
serde_yaml = { version = "0.0.10", package = "serde_yml" }
shlex = "1.1.0"
srv = { git = "https://github.com/epi-project/policy-reasoner" }
tempfile = "3.10.1"
tokio = { version = "1.38.0", features = [] }

Expand All @@ -55,6 +56,7 @@ brane-shr = { path = "../brane-shr" }
brane-tsk = { path = "../brane-tsk" }
specifications = { path = "../specifications" }
clap_complete = "4.5.8"
indoc = "2.0.5"


# Hacky indeed but necessary to dodge OpenSSL linking, which we need to make cross-compilation _so_ much easier
Expand Down
151 changes: 93 additions & 58 deletions brane-ctl/src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ struct CfsslCsrKey {


/***** LIBRARY *****/
/// Handles generating a new `node.yml` config file for a central _or_ worker node.
/// Handles creating a new `node.yml` config file for a central, worker, _or_ proxy node.
///
/// # Arguments
/// - `path`: The path to write the central node.yml to.
Expand All @@ -600,14 +600,39 @@ struct CfsslCsrKey {
///
/// # Errors
/// This function may error if I/O errors occur while writing the file.
pub fn node(
pub fn node (
path: impl Into<PathBuf>,
hosts: Vec<Pair<String, ':', IpAddr>>,
fix_dirs: bool,
config_path: impl Into<PathBuf>,
command: GenerateNodeSubcommand,
) -> Result<(), Error> {
let path: PathBuf = path.into();
// TODO: AsRef Path is probably nicer
let path = path.into();
let node = generate_node(hosts, fix_dirs, config_path, command)?;
write_node(node, &path)
}

/// Handles creating a new NodeConfig object for a central, worker, _or_ proxy node.
///
/// # Arguments
/// - `path`: The path to write the central node.yml to.
/// - `hosts`: List of additional hostnames to set in the launched containers.
/// - `fix_dirs`: if true, will generate missing directories instead of complaining.
/// - `config_path`: The path to the config directory that other paths may use as their base.
/// - `command`: The GenerateSubcommand that contains the specific values to write, as well as whether to write a central or worker node.
///
/// # Returns
/// A NodeConfig object constructed using the provided parameters
///
/// # Errors
/// This function may error if I/O errors occur while writing the file.
pub fn generate_node(
hosts: Vec<Pair<String, ':', IpAddr>>,
fix_dirs: bool,
config_path: impl Into<PathBuf>,
command: GenerateNodeSubcommand,
) -> Result<NodeConfig, Error> {
let config_path: PathBuf = config_path.into();
info!("Generating node.yml for a {}...", match &command {
GenerateNodeSubcommand::Central { .. } => {
Expand Down Expand Up @@ -654,19 +679,24 @@ pub fn node(
plr_port,
} => {
// Remove any scheme, paths, ports, whatever from the hostname
let mut hostname: &str = &hostname;
if let Some(pos) = hostname.find("://") {
hostname = &hostname[pos + 3..];
}
hostname = hostname.split(':').next().unwrap();
hostname = hostname.split('/').next().unwrap();
let hostname = match hostname.split_once("://") {
Some((_scheme, hostname)) => hostname,
None => &hostname
};

let hostname = match hostname.split_once([':', '/']) {
Some((hostname, _port_uri)) => hostname,
None => hostname,
};

// Resolve any path depending on the '$CONFIG'
let infra: PathBuf = resolve_config_path(infra, &config_path);
let proxy: PathBuf = resolve_config_path(proxy, &config_path);
let certs: PathBuf = resolve_config_path(certs, &config_path);

// Ensure the directory structure is there
// TODO: Does not really seem like the responsibility of generating a node as if these
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK good catch xD it should just be an assert instead, then.

// dont exist we probably have bigger problems.
ensure_dir_of(&infra, fix_dirs)?;
ensure_dir_of(&proxy, fix_dirs)?;
ensure_dir(&certs, fix_dirs)?;
Expand Down Expand Up @@ -754,12 +784,15 @@ pub fn node(
chk_port,
} => {
// Remove any scheme, paths, ports, whatever from the hostname
let mut hostname: &str = &hostname;
if let Some(pos) = hostname.find("://") {
hostname = &hostname[pos + 3..];
}
hostname = hostname.split(':').next().unwrap();
hostname = hostname.split('/').next().unwrap();
let hostname = match hostname.split_once("://") {
Some((_scheme, hostname)) => hostname,
None => &hostname
};

let hostname = match hostname.split_once([':', '/']) {
Some((hostname, _port_uri)) => hostname,
None => hostname,
};

// Resolve the service names
let prx_name: String = prx_name.replace("$LOCATION", &location_id);
Expand Down Expand Up @@ -855,12 +888,15 @@ pub fn node(
// Generate the proxy node
GenerateNodeSubcommand::Proxy { hostname, proxy, certs, prx_name, prx_port } => {
// Remove any scheme, paths, ports, whatever from the hostname
let mut hostname: &str = &hostname;
if let Some(pos) = hostname.find("://") {
hostname = &hostname[pos + 3..];
}
hostname = hostname.split(':').next().unwrap();
hostname = hostname.split('/').next().unwrap();
let hostname = match hostname.split_once("://") {
Some((_scheme, hostname)) => hostname,
None => &hostname
};

let hostname = match hostname.split_once([':', '/']) {
Some((hostname, _port_uri)) => hostname,
None => hostname,
};

// Resolve any path depending on the '$CONFIG'
let proxy: PathBuf = resolve_config_path(proxy, &config_path);
Expand Down Expand Up @@ -892,26 +928,29 @@ pub fn node(
},
};

// Done
Ok(node_config)
}

/// Writes a node config to a file. Appends stuff like a header.
/// TODO: This needs to be a method on NodeConfig
fn write_node(node: NodeConfig, path: impl AsRef<Path>) -> Result<(), Error> {
let path = path.as_ref();
// Open the file and write a header to it
debug!("Writing to '{}'...", path.display());
let mut handle: File = match File::create(&path) {
Ok(handle) => handle,
Err(err) => {
return Err(Error::FileCreateError { what: "node.yml", path, err });
},
};
let mut handle = File::create(&path)
.map_err(|err| Error::FileCreateError { what: "node.yml", path: path.to_owned(), err })?;

// Write the top comment header thingy
if let Err(err) = write_node_header(&mut handle) {
return Err(Error::FileHeaderWriteError { what: "infra.yml", path, err });
}
write_node_header(&mut handle)
.map_err(|err| Error::FileHeaderWriteError { what: "infra.yml", path: path.to_owned(), err })?;

// Write the file itself
if let Err(err) = node_config.to_writer(handle, true) {
return Err(Error::FileBodyWriteError { what: "infra.yml", path, err });
}
node.to_writer(handle, true)
.map_err(|err| Error::FileBodyWriteError { what: "infra.yml", path: path.to_owned(), err })?;

// Done
println!("Successfully generated {}", style(path.display().to_string()).bold().green());

Ok(())
}

Expand Down Expand Up @@ -1176,7 +1215,7 @@ pub fn infra(
job_ports: Vec<Pair<String, '=', u16>>,
) -> Result<(), Error> {
let path: PathBuf = path.into();
info!("Generating creds.yml...");
info!("Generating infra.yml...");

// Create the locations
debug!("Generating infrastructure information...");
Expand Down Expand Up @@ -1320,7 +1359,7 @@ pub fn backend(
///
/// # Errors
/// This function may error if I/O errors occur while writing the file.
pub async fn policy_database(fix_dirs: bool, path: PathBuf, branch: String) -> Result<(), Error> {
pub async fn policy_database(fix_dirs: bool, path: PathBuf, branch: &str) -> Result<(), Error> {
info!("Generating policies.db at '{}'...", path.display());

// First, touch the file alive
Expand All @@ -1336,35 +1375,31 @@ pub async fn policy_database(fix_dirs: bool, path: PathBuf, branch: String) -> R
let (_dir, migrations): (TempDir, FileBasedMigrations) = {
// Prepare the input URL and output directory
let url = format!("https://api.github.com/repos/epi-project/policy-reasoner/tarball/{branch}");
let dir = match TempDir::new() {
Ok(dir) => dir,
Err(err) => {
return Err(Error::TempDirError { err });
},
};

let dir = TempDir::new().map_err(|err| Error::TempDirError { err })?;

// Download the file
let tar_path: PathBuf = dir.path().join("repo.tar.gz");
let dir_path: PathBuf = dir.path().join("repo");
if let Err(err) = brane_shr::fs::download_file_async(&url, &tar_path, DownloadSecurity { checksum: None, https: true }, None).await {
return Err(Error::RepoDownloadError { repo: url, target: dir_path, err });
}
if let Err(err) = brane_shr::fs::unarchive_async(&tar_path, &dir_path).await {
return Err(Error::RepoUnpackError { tar: tar_path, target: dir_path, err });
}

brane_shr::fs::download_file_async(&url, &tar_path, DownloadSecurity { checksum: None, https: true }, None)
.await
.map_err(|err| Error::RepoDownloadError { repo: url, target: dir_path.clone(), err })?;

brane_shr::fs::unarchive_async(&tar_path, &dir_path).await.map_err(|err| Error::RepoUnpackError {
tar: tar_path,
target: dir_path.clone(),
err,
})?;

// Resolve that one weird folder in there
let dir_path: PathBuf = match brane_shr::fs::recurse_in_only_child_async(&dir_path).await {
Ok(path) => path,
Err(err) => {
return Err(Error::RepoRecurseError { target: dir_path, err });
},
};
let dir_path: PathBuf =
brane_shr::fs::recurse_in_only_child_async(&dir_path).await.map_err(|err| Error::RepoRecurseError { target: dir_path, err })?;

// Read that as the migrations
let migrations: FileBasedMigrations = match FileBasedMigrations::find_migrations_directory_in_path(&dir_path) {
Ok(migrations) => migrations,
Err(err) => return Err(Error::MigrationsRetrieve { path: dir_path, err }),
};
let migrations: FileBasedMigrations =
FileBasedMigrations::find_migrations_directory_in_path(&dir_path).map_err(|err| Error::MigrationsRetrieve { path: dir_path, err })?;

(dir, migrations)
};

Expand Down
4 changes: 2 additions & 2 deletions brane-ctl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ async fn main() {

GenerateSubcommand::PolicyDatabase { fix_dirs, path, branch } => {
// Call the thing
if let Err(err) = generate::policy_database(fix_dirs, path, branch).await {
if let Err(err) = generate::policy_database(fix_dirs, path, &branch).await {
error!("{}", err.trace());
std::process::exit(1);
}
Expand Down Expand Up @@ -150,7 +150,7 @@ async fn main() {
},
CtlSubcommand::Wizard(subcommand) => match *subcommand {
WizardSubcommand::Setup {} => {
if let Err(err) = wizard::setup() {
if let Err(err) = wizard::Wizard::run().await {
error!("{}", err.trace());
std::process::exit(1);
}
Expand Down
Loading