• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

getdozer / dozer / 5960329125

24 Aug 2023 06:35AM UTC coverage: 75.778% (-0.4%) from 76.201%
5960329125

push

github

web-flow
add mongodb feature to dozer-cli package (#1905)

46996 of 62018 relevant lines covered (75.78%)

89384.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/dozer-cli/src/main.rs
1
use clap::Parser;
2
#[cfg(feature = "cloud")]
3
use dozer_cli::cli::cloud::CloudCommands;
4
use dozer_cli::cli::generate_config_repl;
5
use dozer_cli::cli::types::{Cli, Commands, ConnectorCommand, RunCommands, SecurityCommands};
6
use dozer_cli::cli::{init_dozer, list_sources, LOGO};
7
use dozer_cli::errors::{CliError, CloudError, OrchestrationError};
8
use dozer_cli::simple::SimpleOrchestrator;
9
#[cfg(feature = "cloud")]
10
use dozer_cli::CloudOrchestrator;
11
use dozer_cli::{live, set_ctrl_handler, set_panic_hook, shutdown};
12
use dozer_types::models::telemetry::{TelemetryConfig, TelemetryMetricsConfig};
13
use dozer_types::serde::Deserialize;
14
use dozer_types::tracing::{error, info};
15
use tokio::runtime::Runtime;
16
use tokio::time;
17

18
use clap::CommandFactory;
19
#[cfg(feature = "cloud")]
20
use dozer_cli::cloud_app_context::CloudAppContext;
21
use std::cmp::Ordering;
22
use std::sync::Arc;
23

24
use dozer_types::log::{debug, warn};
25
use std::time::Duration;
26
use std::{env, process};
27

28
fn main() {
×
29
    set_panic_hook();
×
30

31
    if let Err(e) = run() {
×
32
        display_error(&e);
×
33
        process::exit(1);
×
34
    }
×
35
}
×
36

37
fn render_logo() {
×
38
    const VERSION: &str = env!("CARGO_PKG_VERSION");
×
39

×
40
    println!("{LOGO}");
×
41
    println!("\nDozer Version: {VERSION}\n");
×
42
}
×
43

44
#[derive(Deserialize, Debug)]
×
45
#[serde(crate = "dozer_types::serde")]
46
struct DozerPackage {
47
    #[serde(rename(deserialize = "latestVersion"))]
48
    pub latest_version: String,
49
    #[serde(rename(deserialize = "availableAssets"))]
50
    pub _available_assets: Vec<String>,
51
    pub link: String,
52
}
53

×
54
fn version_to_vector(version: &str) -> Vec<i32> {
×
55
    version.split('.').map(|s| s.parse().unwrap()).collect()
×
56
}
×
57

×
58
fn compare_versions(v1: Vec<i32>, v2: Vec<i32>) -> bool {
×
59
    for i in 0..v1.len() {
×
60
        match v1.get(i).cmp(&v2.get(i)) {
×
61
            Ordering::Greater => return true,
×
62
            Ordering::Less => return false,
×
63
            Ordering::Equal => continue,
×
64
        }
65
    }
×
66
    false
×
67
}
×
68

×
69
async fn check_update() {
×
70
    const VERSION: &str = env!("CARGO_PKG_VERSION");
×
71
    let dozer_env = std::env::var("DOZER_ENV").unwrap_or("local".to_string());
×
72
    let dozer_dev = std::env::var("DOZER_DEV").unwrap_or("ext".to_string());
×
73
    let query = vec![
×
74
        ("version", VERSION),
×
75
        ("build", std::env::consts::ARCH),
×
76
        ("os", std::env::consts::OS),
×
77
        ("env", &dozer_env),
×
78
        ("dev", &dozer_dev),
×
79
    ];
×
80

×
81
    let request_url = "https://metadata.dev.getdozer.io/";
×
82

×
83
    let client = reqwest::Client::new();
×
84

×
85
    let mut printed = false;
×
86

87
    loop {
×
88
        let response = client
×
89
            .get(&request_url.to_string())
×
90
            .query(&query)
×
91
            .send()
×
92
            .await;
×
93

×
94
        match response {
×
95
            Ok(r) => {
×
96
                if !printed {
×
97
                    let package: DozerPackage = r.json().await.unwrap();
×
98
                    let current = version_to_vector(VERSION);
×
99
                    let remote = version_to_vector(&package.latest_version);
×
100

×
101
                    if compare_versions(remote, current) {
×
102
                        info!("A new version of Dozer is available.");
×
103
                        info!(
×
104
                            "You can download v{}, from {}.",
×
105
                            package.latest_version, package.link
×
106
                        );
×
107
                        printed = true;
×
108
                    }
×
109
                }
×
110
            }
×
111
            Err(e) => {
×
112
                // We dont show error if error is connection error, because mostly it happens
×
113
                // when main thread is shutting down before request completes.
×
114
                if !e.is_connect() {
×
115
                    warn!("Unable to fetch the latest metadata");
×
116
                }
×
117

×
118
                debug!("Updates check error: {}", e);
×
119
            }
120
        }
×
121
        time::sleep(Duration::from_secs(2 * 60 * 60)).await;
×
122
    }
123
}
124

×
125
fn run() -> Result<(), OrchestrationError> {
×
126
    // Reloading trace layer seems impossible, so we are running Cli::parse in a closure
127
    // and then initializing it after reading the configuration. This is a hacky workaround, but it works.
128

×
129
    let cli = parse_and_generate()?;
×
130
    let mut dozer = init_orchestrator(&cli)?;
×
131
    let (shutdown_sender, shutdown_receiver) = shutdown::new(&dozer.runtime);
×
132
    set_ctrl_handler(shutdown_sender);
×
133

×
134
    // Now we have access to telemetry configuration. Telemetry must be initialized in tokio runtime.
×
135
    let app_name = dozer.config.app_name.clone();
×
136
    let app_id = dozer
×
137
        .config
×
138
        .cloud
×
139
        .as_ref()
×
140
        .map(|cloud| cloud.app_id.clone().unwrap_or(app_name));
×
141

142
    // We always enable telemetry when running live.
×
143
    let telemetry_config = if matches!(cli.cmd, Some(Commands::Live(_))) {
×
144
        Some(TelemetryConfig {
×
145
            trace: None,
×
146
            metrics: Some(TelemetryMetricsConfig::Prometheus(())),
×
147
        })
×
148
    } else {
×
149
        dozer.config.telemetry.clone()
×
150
    };
151

×
152
    let _telemetry = dozer
×
153
        .runtime
×
154
        .block_on(async { Telemetry::new(app_id.as_deref(), telemetry_config) });
×
155

×
156
    if let Some(cmd) = cli.cmd {
×
157
        // run individual servers
×
158
        match cmd {
×
159
            Commands::Run(run) => match run.command {
×
160
                RunCommands::Api => {
×
161
                    render_logo();
×
162

×
163
                    dozer.run_api(shutdown_receiver)
×
164
                }
165
                RunCommands::App => {
×
166
                    render_logo();
×
167

×
168
                    dozer.run_apps(shutdown_receiver, None)
×
169
                }
170
            },
×
171
            Commands::Security(security) => match security.command {
×
172
                SecurityCommands::GenerateToken => {
×
173
                    let token = dozer.generate_token()?;
×
174
                    info!("token: {:?} ", token);
×
175
                    Ok(())
×
176
                }
177
            },
×
178
            Commands::Build(build) => {
×
179
                let force = build.force.is_some();
×
180

×
181
                dozer.build(force, shutdown_receiver)
×
182
            }
×
183
            Commands::Connectors(ConnectorCommand { filter }) => {
×
184
                dozer.runtime.block_on(list_sources(
×
185
                    dozer.runtime.clone(),
×
186
                    cli.config_paths,
×
187
                    cli.config_token,
×
188
                    cli.config_overrides,
×
189
                    cli.ignore_pipe,
×
190
                    filter,
×
191
                ))
×
192
            }
×
193
            Commands::Clean => dozer.clean(),
×
194
            #[cfg(feature = "cloud")]
195
            Commands::Cloud(cloud) => {
196
                render_logo();
197

198
                match cloud.command.clone() {
199
                    CloudCommands::Deploy(deploy) => dozer.deploy(cloud, deploy, cli.config_paths),
200
                    CloudCommands::Api(api) => dozer.api(cloud, api),
201
                    CloudCommands::Login {
202
                        organisation_slug,
203
                        profile_name,
204
                        client_id,
205
                        client_secret,
206
                    } => dozer.login(
207
                        cloud,
208
                        organisation_slug,
209
                        profile_name,
210
                        client_id,
211
                        client_secret,
212
                    ),
213
                    CloudCommands::Secrets(command) => {
214
                        dozer.execute_secrets_command(cloud, command)
215
                    }
216
                    CloudCommands::Delete => dozer.delete(cloud),
217
                    CloudCommands::Status => dozer.status(cloud),
218
                    CloudCommands::Monitor => dozer.monitor(cloud),
219
                    CloudCommands::Logs(logs) => dozer.trace_logs(cloud, logs),
220
                    CloudCommands::Version(version) => dozer.version(cloud, version),
221
                    CloudCommands::List(list) => dozer.list(cloud, list),
222
                    CloudCommands::SetApp { app_id } => {
223
                        CloudAppContext::save_app_id(app_id.clone())?;
224
                        info!("Using \"{app_id}\" app");
225
                        Ok(())
226
                    }
227
                }
228
            }
229
            Commands::Init => {
×
230
                panic!("This should not happen as it is handled in parse_and_generate");
×
231
            }
×
232
            Commands::Live(live_flags) => {
×
233
                render_logo();
×
234
                dozer.runtime.block_on(live::start_live_server(
×
235
                    &dozer.runtime,
×
236
                    shutdown_receiver,
×
237
                    live_flags,
×
238
                ))?;
×
239
                Ok(())
×
240
            }
241
        }
242
    } else {
×
243
        render_logo();
×
244

×
245
        dozer.run_all(shutdown_receiver)
×
246
    }
×
247
}
×
248

249
// Some commands dont need to initialize the orchestrator
250
// This function is used to run those commands
×
251
fn parse_and_generate() -> Result<Cli, OrchestrationError> {
×
252
    dozer_tracing::init_telemetry_closure(None, None, || -> Result<Cli, OrchestrationError> {
×
253
        let cli = Cli::parse();
×
254

×
255
        if let Some(Commands::Init) = cli.cmd {
×
256
            Telemetry::new(None, None);
×
257
            if let Err(e) = generate_config_repl() {
×
258
                error!("{}", e);
×
259
                Err(e)
×
260
            } else {
261
                // We need to exit here, otherwise the orchestrator will be initialized
×
262
                process::exit(0);
×
263
            }
264
        } else {
×
265
            Ok(cli)
×
266
        }
×
267
    })
×
268
}
×
269

×
270
fn init_orchestrator(cli: &Cli) -> Result<SimpleOrchestrator, CliError> {
×
271
    dozer_tracing::init_telemetry_closure(None, None, || -> Result<SimpleOrchestrator, CliError> {
×
272
        let runtime = Arc::new(Runtime::new().map_err(CliError::FailedToCreateTokioRuntime)?);
×
273
        let res = runtime.block_on(init_dozer(
×
274
            runtime.clone(),
×
275
            cli.config_paths.clone(),
×
276
            cli.config_token.clone(),
×
277
            cli.config_overrides.clone(),
×
278
            cli.ignore_pipe,
×
279
            cli.enable_progress,
×
280
        ));
×
281

×
282
        match res {
×
283
            Ok(dozer) => {
×
284
                dozer.runtime.spawn(check_update());
×
285
                Ok(dozer)
×
286
            }
×
287
            Err(e) => {
×
288
                if let CliError::FailedToFindConfigurationFiles(_) = &e {
×
289
                    let description = "Dozer was not able to find configuration files. \n\n\
×
290
                    Please use \"dozer init\" to create project or \"dozer -c {path}\" with path to your configuration.\n\
×
291
                    Configuration documentation can be found in https://getdozer.io/docs/configuration";
×
292

×
293
                    let mut command = Cli::command();
×
294
                    command = command.about(format!("\n\n\n{} \n {}", LOGO, description));
×
295

×
296
                    println!("{}", command.render_help());
×
297
                }
×
298

×
299
                error!("{}", e);
×
300
                Err(e)
×
301
            }
302
        }
×
303
    })
×
304
}
×
305

306
fn display_error(e: &OrchestrationError) {
×
307
    if let OrchestrationError::CloudError(CloudError::ApplicationNotFound) = &e {
×
308
        let description = "Dozer cloud service was not able to find application. \n\n\
×
309
        Please check your application id in `dozer-config.cloud.yaml` file.\n\
×
310
        To change it, you can manually update file or use \"dozer cloud set-app {app_id}\".";
×
311

×
312
        error!("{}", description);
×
313
    } else {
×
314
        error!("{}", e);
×
315
    }
×
316
}
×
317

318
struct Telemetry();
319

320
impl Telemetry {
×
321
    fn new(app_name: Option<&str>, config: Option<TelemetryConfig>) -> Self {
×
322
        dozer_tracing::init_telemetry(app_name, config);
×
323
        Self()
×
324
    }
×
325
}
326

327
impl Drop for Telemetry {
×
328
    fn drop(&mut self) {
×
329
        dozer_tracing::shutdown_telemetry();
×
330
    }
×
331
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc