Skip to main content

mz_deploy/cli/commands/
apply_all.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Apply-all orchestrator — runs all infrastructure apply steps in dependency order.
11//!
12//! Dependency order: clusters → roles → network policies → secrets → connections → sources → tables.
13//!
14//! **Key Insight:** The ordering ensures referential integrity — clusters must
15//! exist before MVs can reference them, connections must exist before sources
16//! and sinks can use them, and sources must exist before `CREATE TABLE FROM
17//! SOURCE` can reference them. On partial failure, objects created in earlier
18//! phases remain in place (all operations are idempotent) and re-running
19//! `apply-all` will skip already-existing objects and retry only the failed
20//! phase onward.
21//!
22//! **Requires:** `materialize_deployer` role (enforced before any phase runs).
23
24use crate::cli::CliError;
25use crate::cli::executor::{ApplyPlan, DeploymentExecutor, compile_apply_project_and_connect};
26use crate::config::Settings;
27
28/// Run all infrastructure apply steps in dependency order.
29///
30/// Plans all phases first with a shared client, then executes if not dry-run.
31/// Applies: clusters → roles → network policies → secrets (unless skipped) → connections → sources → tables.
32pub async fn run(
33    settings: &Settings,
34    skip_secrets: bool,
35    dry_run: bool,
36) -> Result<ApplyPlan, CliError> {
37    let (planned_project, client) = compile_apply_project_and_connect(settings).await?;
38
39    let mut plan = ApplyPlan::new();
40    let executor = DeploymentExecutor::new_dry_run(&client);
41
42    // Infrastructure phases (no schemas needed)
43    plan.add_phase(super::clusters::plan(settings, &client, &executor).await?);
44    plan.add_phase(super::roles::plan(settings, &client, &executor).await?);
45    plan.add_phase(super::apply_network_policies::plan(settings, &client, &executor).await?);
46
47    // Database object phases (schemas deduplicated via plan)
48    if !skip_secrets {
49        let phase =
50            super::apply_secrets::plan(settings, &client, &executor, &planned_project, &mut plan)
51                .await?;
52        plan.add_phase(phase);
53    }
54
55    let phase =
56        super::apply_connections::plan(settings, &client, &executor, &planned_project, &mut plan)
57            .await?;
58    plan.add_phase(phase);
59
60    let phase =
61        super::apply_sources::plan(settings, &client, &executor, &planned_project, &mut plan)
62            .await?;
63    plan.add_phase(phase);
64
65    let phase =
66        super::apply_tables::plan(settings, &client, &executor, &planned_project, &mut plan)
67            .await?;
68    plan.add_phase(phase);
69
70    if !dry_run {
71        plan.execute(&client).await?;
72        super::lock::run(settings).await?;
73    }
74
75    Ok(plan)
76}