From 95f388ca965466407f03bac23733fed143334fad Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Thu, 19 Dec 2024 19:46:31 -0300 Subject: [PATCH] fix(repo): use cli instead of config.toml --- .github/workflows/docker_publish.yaml | 18 ++- crates/fuel-streams-ws/config.toml | 14 --- crates/fuel-streams-ws/src/cli.rs | 63 ++++++++-- crates/fuel-streams-ws/src/config.rs | 161 ++++---------------------- crates/fuel-streams-ws/src/lib.rs | 4 +- crates/fuel-streams-ws/src/main.rs | 33 +----- 6 files changed, 102 insertions(+), 191 deletions(-) delete mode 100644 crates/fuel-streams-ws/config.toml diff --git a/.github/workflows/docker_publish.yaml b/.github/workflows/docker_publish.yaml index 43de8f3f..748ee112 100644 --- a/.github/workflows/docker_publish.yaml +++ b/.github/workflows/docker_publish.yaml @@ -2,6 +2,16 @@ name: Build and publish Docker image on: workflow_dispatch: + inputs: + image_type: + description: "Choose which image to build (publisher/webserver/both)" + required: true + type: choice + options: + - publisher + - webserver + - both + default: "both" push: branches: - main @@ -32,7 +42,9 @@ jobs: id: sha run: echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Build and push Docker (${{ steps.sha.outputs.short_sha }}) for publisher + - name: Build and push Docker for publisher + if: github.event.inputs.image_type == 'publisher' || github.event.inputs.image_type == 'both' + || github.event_name != 'workflow_dispatch' uses: ./.github/actions/docker-publish id: publish-fuel-streams-nats with: @@ -41,7 +53,9 @@ jobs: image: ghcr.io/fuellabs/fuel-streams-publisher dockerfile: cluster/docker/fuel-streams-publisher.Dockerfile - - name: Build and push Docker (${{ steps.sha.outputs.short_sha }}) for webserver + - name: Build and push Docker for webserver + if: github.event.inputs.image_type == 'webserver' || github.event.inputs.image_type == 'both' + || github.event_name != 'workflow_dispatch' uses: ./.github/actions/docker-publish id: publish-fuel-webserver-nats with: diff --git a/crates/fuel-streams-ws/config.toml b/crates/fuel-streams-ws/config.toml deleted file mode 100644 index 1c71a7a4..00000000 --- a/crates/fuel-streams-ws/config.toml +++ /dev/null @@ -1,14 +0,0 @@ -[api] -port = 9003 - -[nats] -network = "local" - -[fuel] -network = "local" - -[s3] -enabled = true - -[auth] -jwt-secret = "secret" diff --git a/crates/fuel-streams-ws/src/cli.rs b/crates/fuel-streams-ws/src/cli.rs index b27bb40a..29c5c25f 100644 --- a/crates/fuel-streams-ws/src/cli.rs +++ b/crates/fuel-streams-ws/src/cli.rs @@ -1,17 +1,64 @@ use clap::Parser; /// CLI structure for parsing command-line arguments. -/// -/// - `config_path`: Path to the toml config file. #[derive(Clone, Parser)] pub struct Cli { - /// Config path + /// API port number #[arg( long, - value_name = "CONFIG", - env = "CONFIG_PATH", - default_value = "config.toml", - help = "Path to toml config file" + value_name = "PORT", + env = "API_PORT", + default_value = "9003", + help = "Port number for the API server" )] - pub config_path: Option, + pub api_port: u16, + + /// NATS URL + #[arg( + long, + value_name = "NATS_URL", + env = "NATS_URL", + default_value = "nats://localhost:4222", + help = "NATS URL" + )] + pub nats_url: String, + + /// Fuel network configuration + #[arg( + long, + value_name = "NETWORK", + env = "NETWORK", + default_value = "local", + help = "Fuel network configuration (local, etc.)" + )] + pub network: String, + + /// Enable S3 + #[arg( + long, + value_name = "AWS_S3_ENABLED", + env = "AWS_S3_ENABLED", + default_value = "true", + help = "Enable S3 integration" + )] + pub s3_enabled: bool, + + /// JWT secret + #[arg( + long, + value_name = "JWT_AUTH_SECRET", + env = "JWT_AUTH_SECRET", + default_value = "secret", + help = "Secret key for JWT authentication" + )] + pub jwt_secret: String, + + /// Use metrics + #[arg( + long, + env = "USE_METRICS", + default_value = "false", + help = "Enable metrics" + )] + pub use_metrics: bool, } diff --git a/crates/fuel-streams-ws/src/config.rs b/crates/fuel-streams-ws/src/config.rs index e90e294c..910fa2eb 100644 --- a/crates/fuel-streams-ws/src/config.rs +++ b/crates/fuel-streams-ws/src/config.rs @@ -1,79 +1,49 @@ -use std::{ - num::ParseIntError, - path::{Path, PathBuf}, - str::{FromStr, ParseBoolError}, - time::Duration, -}; +use std::{path::PathBuf, str::FromStr}; -use confy::ConfyError; +use clap::Parser; use displaydoc::Display as DisplayDoc; use fuel_streams::types::FuelNetwork; -use serde::{Deserialize, Deserializer}; use thiserror::Error; -use tokio::{fs::File, io::AsyncReadExt}; #[derive(Debug, DisplayDoc, Error)] pub enum Error { - /// Open config file: {0} - OpenConfig(std::io::Error), - /// Failed to parse config: {0} - ParseConfig(toml::de::Error), - /// Failed to parse config as utf-8: {0} - ParseUtf8(std::string::FromUtf8Error), - /// Failed to read config file: {0} - ReadConfig(std::io::Error), - /// Failed to read config metadata: {0} - ReadMeta(std::io::Error), - /// Failed to read env config: {0} - Confy(ConfyError), /// Undecodable config element: {0} UndecodableConfigElement(&'static str), - /// Parse int error: {0} - ParseInt(ParseIntError), - /// Parse bool error: {0} - ParseBool(ParseBoolError), } -#[derive(Debug, Default, Deserialize, Clone)] -#[serde(deny_unknown_fields, rename_all = "kebab-case")] +#[derive(Debug, Default, Clone)] pub struct S3Config { pub enabled: bool, } -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "kebab-case")] +#[derive(Clone, Debug)] pub struct TlsConfig { pub private_key: PathBuf, pub certificate: PathBuf, } -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "kebab-case")] +#[derive(Clone, Debug)] pub struct ApiConfig { pub port: u16, pub tls: Option, } -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "kebab-case")] +#[derive(Clone, Debug)] pub struct AuthConfig { pub jwt_secret: String, } -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "kebab-case")] +#[derive(Clone, Debug)] pub struct FuelConfig { pub network: FuelNetwork, } -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "kebab-case")] +#[derive(Clone, Debug)] pub struct NatsConfig { pub network: FuelNetwork, } -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "kebab-case")] +#[derive(Clone, Debug)] pub struct Config { pub api: ApiConfig, pub auth: AuthConfig, @@ -82,112 +52,31 @@ pub struct Config { pub fuel: FuelConfig, } -impl Default for Config { - fn default() -> Self { - Config { +impl Config { + pub fn load() -> Result { + let cli = crate::cli::Cli::parse(); + Self::from_cli(&cli) + } + + fn from_cli(cli: &crate::cli::Cli) -> Result { + Ok(Config { api: ApiConfig { - port: 9003, + port: cli.api_port, tls: None, }, auth: AuthConfig { - jwt_secret: String::new(), + jwt_secret: cli.jwt_secret.clone(), }, nats: NatsConfig { network: FuelNetwork::Local, }, - s3: S3Config { enabled: false }, + s3: S3Config { + enabled: cli.s3_enabled, + }, fuel: FuelConfig { - network: FuelNetwork::Local, + network: FuelNetwork::from_str(&cli.network) + .map_err(|_| Error::UndecodableConfigElement("NETWORK"))?, }, - } - } -} - -#[allow(dead_code)] -fn deserialize_duration_from_usize<'de, D>( - deserializer: D, -) -> Result -where - D: Deserializer<'de>, -{ - let seconds = u64::deserialize(deserializer)?; - Ok(Duration::from_secs(seconds)) -} - -#[allow(dead_code)] -fn deserialize_duration_option<'de, D>( - deserializer: D, -) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let seconds: Option = Option::deserialize(deserializer)?; - if seconds.is_none() { - return Ok(None); - } - Ok(seconds.map(Duration::from_secs)) -} - -impl Config { - pub async fn from_path( - path: impl AsRef + Send, - ) -> Result { - read_to_string(path).await?.parse() + }) } - - pub fn from_envs() -> Result { - let mut config = Self::default(); - - // ----------------------API-------------------------------- - if let Ok(app_port) = dotenvy::var("STREAMER_API_PORT") { - config.api.port = - app_port.parse::().map_err(Error::ParseInt)?; - } - - // ----------------------NATS-------------------------------- - if let Ok(nats_network) = dotenvy::var("NETWORK") { - config.nats.network = FuelNetwork::from_str(&nats_network) - .map_err(|_| Error::UndecodableConfigElement("NETWORK"))?; - } - - // ----------------------S3-------------------------------- - if let Ok(s3_enabled) = dotenvy::var("AWS_S3_ENABLED") { - config.s3.enabled = - s3_enabled.parse::().map_err(Error::ParseBool)?; - } - - // ----------------------AUTH-------------------------------- - if let Ok(jwt_secret) = dotenvy::var("JWT_AUTH_SECRET") { - config.auth.jwt_secret = jwt_secret; - } - - // ----------------------FUEL-------------------------------- - if let Ok(network) = dotenvy::var("NETWORK") { - config.fuel.network = FuelNetwork::from_str(&network) - .map_err(|_| Error::UndecodableConfigElement("NETWORK"))?; - } - - Ok(config) - } -} - -impl FromStr for Config { - type Err = Error; - - fn from_str(s: &str) -> Result { - toml::from_str(s).map_err(Error::ParseConfig) - } -} - -async fn read_to_string( - path: impl AsRef + Send, -) -> Result { - let mut file = File::open(path).await.map_err(Error::OpenConfig)?; - let meta = file.metadata().await.map_err(Error::ReadMeta)?; - let mut contents = - Vec::with_capacity(usize::try_from(meta.len()).unwrap_or(0)); - file.read_to_end(&mut contents) - .await - .map_err(Error::ReadConfig)?; - String::from_utf8(contents).map_err(Error::ParseUtf8) } diff --git a/crates/fuel-streams-ws/src/lib.rs b/crates/fuel-streams-ws/src/lib.rs index d60b1e6c..e9c80f91 100644 --- a/crates/fuel-streams-ws/src/lib.rs +++ b/crates/fuel-streams-ws/src/lib.rs @@ -4,13 +4,13 @@ pub mod config; pub mod server; pub mod telemetry; -use std::{env, sync::LazyLock}; +use std::sync::LazyLock; pub static STREAMER_MAX_WORKERS: LazyLock = LazyLock::new(|| { let available_cpus = num_cpus::get(); let default_threads = 2 * available_cpus; - env::var("STREAMER_MAX_WORKERS") + dotenvy::var("STREAMER_MAX_WORKERS") .ok() .and_then(|val| val.parse().ok()) .unwrap_or(default_threads) diff --git a/crates/fuel-streams-ws/src/main.rs b/crates/fuel-streams-ws/src/main.rs index f8d6fe90..e629bd31 100644 --- a/crates/fuel-streams-ws/src/main.rs +++ b/crates/fuel-streams-ws/src/main.rs @@ -1,7 +1,4 @@ -use anyhow::Context as _; -use clap::Parser; use fuel_streams_ws::{ - cli::Cli, config::Config, server::{api::create_api, context::Context, state::ServerState}, }; @@ -20,36 +17,14 @@ async fn main() -> anyhow::Result<()> { .with_span_events(FmtSpan::CLOSE) .init(); - // load envs - dotenvy::dotenv().context("Failed to env values")?; + if let Err(err) = dotenvy::dotenv() { + tracing::error!("File .env not found: {:?}", err); + } - // read cli args - let cli = Cli::parse(); - - // load config - let config = match cli.config_path { - Some(path) => { - tracing::info!("Using config file: {}", path); - Config::from_path(path) - .await - .context("Failed to load toml config")? - } - None => { - tracing::info!("Using envs to load config"); - Config::from_envs().context("Failed to load toml config")? - } - }; - - // init context + let config = Config::load()?; let context = Context::new(&config).await?; - - // init server shared state let state = ServerState::new(context).await; - - // create the actix webserver let server = create_api(&config, state)?; - - // get server handle let server_handle = server.handle(); // spawn the server in the background