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}