Some checks failed
Run Check Script / check (pull_request) Failing after 1m52s
193 lines
7.3 KiB
Rust
193 lines
7.3 KiB
Rust
//! Install the harmony fleet server-side stack into the cluster
|
|
//! `KUBECONFIG` points at: NATS + the harmony fleet operator (CRDs +
|
|
//! RBAC + Deployment), and optionally a central Zitadel OIDC
|
|
//! identity provider, via [`FleetServerScore`].
|
|
//!
|
|
//! This is the framework-side replacement for the
|
|
//! `example_fleet_nats_install`, `harmony-fleet-operator chart`,
|
|
//! and `helm install` chain that the load-test harness used to
|
|
//! drive by hand.
|
|
//!
|
|
//! Typical usage (operator + NATS only):
|
|
//!
|
|
//! KUBECONFIG=$KUBECFG cargo run -q -p example_fleet_server_install -- \
|
|
//! --operator-image hub.nationtech.io/harmony/harmony-fleet-operator:dev
|
|
//!
|
|
//! Including Zitadel:
|
|
//!
|
|
//! KUBECONFIG=$KUBECFG cargo run -q -p example_fleet_server_install -- \
|
|
//! --operator-image … \
|
|
//! --zitadel-host zitadel.localhost
|
|
//!
|
|
//! Behaviour:
|
|
//! - Installs single-node NATS (JetStream) into `--nats-namespace`
|
|
//! using `NatsBasicScore`, exposed per `--nats-expose`.
|
|
//! - Installs the operator chart into `--operator-namespace` via
|
|
//! `FleetOperatorScore` (which renders the chart in a tempdir
|
|
//! and helm-installs it).
|
|
//! - When `--zitadel-host` is set, also runs `ZitadelScore`:
|
|
//! provisions a CNPG PostgreSQL cluster + the upstream
|
|
//! `zitadel/zitadel` helm chart with distribution-aware ingress.
|
|
//! Defaults to HTTPS unless host endswith `.localhost` or
|
|
//! `--zitadel-insecure` is passed.
|
|
//! - Idempotent: re-running on an existing install short-circuits
|
|
//! at `HelmChartScore::find_installed_release`.
|
|
//!
|
|
//! Topology: `K8sAnywhereTopology::from_env()`. This requires `KUBECONFIG`
|
|
//! to be set and runs `CertificateManagementScore` as part of
|
|
//! `ensure_ready` — i.e. it installs cert-manager into the cluster on
|
|
//! first run. Cert-manager is needed for Zitadel's ingress TLS in
|
|
//! production; for k3d dev it's still installed but unused.
|
|
//!
|
|
//! Output is driven by `harmony_cli::run`, which wires up the
|
|
//! framework's standard logger + reporter — emoji-tagged progress
|
|
//! lines per Score, plus an end-of-run summary listing the
|
|
//! `Outcome.details` from each Score.
|
|
|
|
use anyhow::Result;
|
|
use clap::Parser;
|
|
use harmony::inventory::Inventory;
|
|
use harmony::modules::nats::NatsBasicScore;
|
|
use harmony::modules::zitadel::ZitadelScore;
|
|
use harmony::score::Score;
|
|
use harmony::topology::K8sAnywhereTopology;
|
|
use harmony_fleet_deploy::FleetOperatorScore;
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(
|
|
name = "fleet_server_install",
|
|
about = "Install the harmony fleet server-side stack (NATS + operator [+ Zitadel])"
|
|
)]
|
|
struct Cli {
|
|
/// Namespace for the NATS Deployment + Service.
|
|
#[arg(long, default_value = "fleet-system")]
|
|
nats_namespace: String,
|
|
/// Resource name for the NATS release.
|
|
#[arg(long, default_value = "fleet-nats")]
|
|
nats_name: String,
|
|
/// NATS service exposure mode. `load-balancer` pairs with k3d's
|
|
/// `-p PORT:PORT@loadbalancer`. `node-port` requires the port be
|
|
/// in the apiserver's nodeport range (default 30000-32767).
|
|
#[arg(long, value_enum, default_value_t = NatsExpose::LoadBalancer)]
|
|
nats_expose: NatsExpose,
|
|
/// NodePort when `--nats-expose=node-port`. Ignored otherwise.
|
|
#[arg(long, default_value_t = 30422)]
|
|
nats_node_port: i32,
|
|
/// Optional NATS image override (`repository:tag`).
|
|
#[arg(long)]
|
|
nats_image: Option<String>,
|
|
|
|
/// Namespace the operator runs in.
|
|
#[arg(long, default_value = "fleet-system")]
|
|
operator_namespace: String,
|
|
/// Helm release name for the operator chart.
|
|
#[arg(long, default_value = "harmony-fleet-operator")]
|
|
operator_release: String,
|
|
/// Operator container image (`repository:tag`).
|
|
#[arg(
|
|
long,
|
|
default_value = "hub.nationtech.io/harmony/harmony-fleet-operator:dev"
|
|
)]
|
|
operator_image: String,
|
|
/// Image pull policy for the operator Deployment.
|
|
#[arg(long, default_value = "IfNotPresent")]
|
|
operator_image_pull_policy: String,
|
|
/// `RUST_LOG` value injected into the operator pod's env.
|
|
#[arg(long, default_value = "info,kube_runtime=warn")]
|
|
log_level: String,
|
|
|
|
/// Hostname Zitadel should answer on. When set, Zitadel + its
|
|
/// PostgreSQL cluster are installed alongside the operator.
|
|
/// When unset, the Zitadel install is skipped entirely.
|
|
#[arg(long)]
|
|
zitadel_host: Option<String>,
|
|
/// Zitadel chart version (matches `zitadel/zitadel` upstream tags).
|
|
#[arg(long, default_value = "v4.12.1")]
|
|
zitadel_version: String,
|
|
/// Force HTTP instead of HTTPS for the Zitadel ingress. Defaults
|
|
/// to true (HTTP) when `--zitadel-host` endswith `.localhost`,
|
|
/// false otherwise.
|
|
#[arg(long)]
|
|
zitadel_insecure: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug, clap::ValueEnum)]
|
|
enum NatsExpose {
|
|
ClusterIp,
|
|
NodePort,
|
|
LoadBalancer,
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
let cli = Cli::parse();
|
|
|
|
let topology = K8sAnywhereTopology::from_env();
|
|
|
|
let mut nats = NatsBasicScore::new(&cli.nats_name, &cli.nats_namespace);
|
|
match cli.nats_expose {
|
|
NatsExpose::ClusterIp => {}
|
|
NatsExpose::NodePort => nats = nats.node_port(cli.nats_node_port),
|
|
NatsExpose::LoadBalancer => nats = nats.load_balancer(),
|
|
}
|
|
if let Some(image) = cli.nats_image {
|
|
nats = nats.image(image);
|
|
}
|
|
|
|
// Point the operator at NATS via the in-cluster service DNS the
|
|
// NatsBasicScore install creates. ClusterIP and LoadBalancer both
|
|
// expose the same `<release>.<namespace>:4222` for in-cluster
|
|
// callers.
|
|
let nats_url = format!("nats://{}.{}:4222", cli.nats_name, cli.nats_namespace);
|
|
|
|
let operator = FleetOperatorScore::new()
|
|
.namespace(&cli.operator_namespace)
|
|
.release_name(&cli.operator_release)
|
|
.image(&cli.operator_image)
|
|
.image_pull_policy(&cli.operator_image_pull_policy)
|
|
.nats_url(&nats_url)
|
|
.log_level(&cli.log_level);
|
|
|
|
// FleetServerScore now takes NatsK8sScore (auth-callout-aware,
|
|
// OKD-Route-aware) — see `fleet_staging_install` for the
|
|
// production composition. This simpler example registers the
|
|
// inner Scores directly so it can keep using the basic NATS
|
|
// helm chart for k3d-style local installs.
|
|
let mut scores: Vec<Box<dyn Score<K8sAnywhereTopology>>> =
|
|
vec![Box::new(nats), Box::new(operator)];
|
|
|
|
if let Some(host) = cli.zitadel_host {
|
|
// Default external_secure logic: HTTPS unless the host is a
|
|
// .localhost / .test development hostname or --zitadel-insecure
|
|
// was explicitly set.
|
|
let external_secure =
|
|
!cli.zitadel_insecure && !host.ends_with(".localhost") && !host.ends_with(".test");
|
|
scores.push(Box::new(ZitadelScore {
|
|
host,
|
|
zitadel_version: cli.zitadel_version,
|
|
external_secure,
|
|
external_port: None,
|
|
..Default::default()
|
|
}));
|
|
}
|
|
|
|
// We've already parsed our own Cli; pass `Some(harmony_cli::Args)`
|
|
// with dev-friendly defaults (no confirmation prompt, run every
|
|
// registered score) so harmony_cli doesn't try to re-parse argv.
|
|
harmony_cli::run(
|
|
Inventory::empty(),
|
|
topology,
|
|
scores,
|
|
Some(harmony_cli::Args {
|
|
yes: true,
|
|
filter: None,
|
|
interactive: false,
|
|
all: true,
|
|
number: 0,
|
|
list: false,
|
|
}),
|
|
)
|
|
.await
|
|
.map_err(|e| anyhow::anyhow!("{e}"))
|
|
}
|