Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
729 changes: 726 additions & 3 deletions rust/Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ tokio = "1"
log = "0.4"
tracing = "0.1"
url = "2"
regex = "1"
schemars = "1"
jsonschema = "0.49"

[profile.release]
lto = "fat"
Expand Down
2 changes: 1 addition & 1 deletion rust/crates/adc-backend-apisix/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::operator::Operator;
use crate::validator::Validator;

/// Shared by `ping` and `resolved_version` — neither needs the response body.
const PROBE_PATH: &str = "/apisix/admin/routes?page=1&page_size=1";
const PROBE_PATH: &str = "/apisix/admin/routes?page=1&page_size=10";

pub struct Backend {
client: HttpClient,
Expand Down
6 changes: 5 additions & 1 deletion rust/crates/adc-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ async fn cmd_diff(args: DiffArgs) -> Result<(), CliError> {
&exclude,
&label_selector,
args.backend.managed_by_label,
args.lint,
),
)
.await?;
Expand Down Expand Up @@ -124,6 +125,7 @@ async fn cmd_sync(args: SyncArgs) -> Result<(), CliError> {
&exclude,
&label_selector,
args.backend.managed_by_label,
args.lint,
),
)
.await?;
Expand Down Expand Up @@ -226,10 +228,11 @@ async fn cmd_lint(args: LintArgs) -> Result<(), CliError> {
&empty_types,
&empty_labels,
false,
true,
),
)
.await?;
println!("Configuration is structurally valid.");
println!("Configuration is valid.");
Ok(())
}

Expand All @@ -246,6 +249,7 @@ async fn cmd_validate(args: ValidateArgs) -> Result<(), CliError> {
&exclude,
&label_selector,
args.backend.managed_by_label,
args.lint,
),
)
.await?;
Expand Down
35 changes: 27 additions & 8 deletions rust/crates/adc-cli/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,20 +175,21 @@ fn resource_filter(args: &BackendArgs) -> Result<ResourceFilter, CliError> {
})
}

/// Loads, merges, and structurally parses the local configuration file(s).
/// Deserializing into `Configuration` here is the structural-validity gate
/// (unknown fields, wrong types, missing required fields all reject —
/// except inside a plugin config body: `Plugin`/`Plugins` are bare maps,
/// deliberately not `deny_unknown_fields`, since ADC can't know every
/// plugin's own schema) — the separate `--no-lint`/`Lint` step has nothing
/// left to check yet, since semantic validation (regex/cross-field rules)
/// hasn't landed (stage 2.2).
/// Loads, merges, and structurally parses the local configuration file(s),
/// then (unless `lint` is `false`, i.e. `--no-lint`) runs semantic
/// validation on top. Deserializing into `Configuration` is the
/// structural-validity gate (unknown fields, wrong types, missing required
/// fields all reject — except inside a plugin config body: `Plugin`/
/// `Plugins` are bare maps, deliberately not `deny_unknown_fields`, since
/// ADC can't know every plugin's own schema) and always runs, regardless of
/// `lint` — only the semantic pass (`adc_sdk::lint::lint`) is skippable.
pub async fn load_local(
files: &[PathBuf],
include: &HashSet<ResourceType>,
exclude: &HashSet<ResourceType>,
label_selector: &HashMap<String, String>,
managed_by_label: bool,
lint: bool,
) -> Result<Configuration, CliError> {
let files = config::read_files(files).await?;
let mut merged = config::merge_files(files)?;
Expand All @@ -199,9 +200,27 @@ pub async fn load_local(
let mut configuration: Configuration = serde_json::from_value(merged)
.map_err(|e| CliError::msg(format!("invalid configuration: {e}")))?;
config::filter_resource_types(&mut configuration, include, exclude);
if lint {
let issues = adc_sdk::lint::lint(&configuration);
if !issues.is_empty() {
return Err(CliError::msg(format_lint_issues(&issues)));
}
}
Ok(configuration)
}

/// Collects every lint violation into one multi-line message — mirrors the
/// TS CLI wrapping `z.prettifyError`'s multi-issue output into a single
/// thrown `Error`.
fn format_lint_issues(issues: &[adc_sdk::lint::LintIssue]) -> String {
let mut message = "Lint configuration\nThe following errors were found in configuration:\n".to_string();
for issue in issues {
message.push_str(&format!(" - {issue}\n"));
}
message.pop();
message
}

/// Converts each OpenAPI document into its own `Configuration`, then
/// flattens their `services` into one — rejecting outright if two
/// documents produce a same-named service, since a resource's id is
Expand Down
2 changes: 1 addition & 1 deletion rust/crates/adc-converter-openapi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ serde_json = { workspace = true }
serde_yaml_ng = "0.10"
unicode-normalization = "0.1"
url = { workspace = true }
regex = "1"
regex = { workspace = true }
thiserror = { workspace = true }
log = { workspace = true }

Expand Down
6 changes: 6 additions & 0 deletions rust/crates/adc-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ sha1 = { workspace = true }
async-trait = { workspace = true }
semver = { workspace = true }
thiserror = { workspace = true }
schemars = { workspace = true }
jsonschema = { workspace = true }

[[bin]]
name = "export-schema"
path = "src/bin/export_schema.rs"
17 changes: 17 additions & 0 deletions rust/crates/adc-sdk/src/bin/export_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! Regenerates `rust/schema.json` from the current `resources::Configuration`
//! shape. Not part of the crate's public library API or the shipped `adc`
//! CLI — a dev-only tool, mirroring the TS SDK's own `nx run cli:export-schema`
//! (`apps/cli/src/linter/exporter.ts`), which is likewise a standalone
//! script rather than an `adc` subcommand.
//!
//! Usage: `cargo run -p adc-sdk --bin export-schema`, from the workspace
//! root — the output path is anchored to this crate's manifest dir (same
//! convention as `tests/schema_json.rs`'s drift check), not the cwd.

const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../schema.json");

fn main() {
let schema = schemars::schema_for!(adc_sdk::resources::Configuration);
let json = serde_json::to_string_pretty(&schema).expect("schema serializes to JSON") + "\n";
std::fs::write(SCHEMA_PATH, json).expect("writing schema.json");
}
15 changes: 9 additions & 6 deletions rust/crates/adc-sdk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
//! ADC's core data model: resource type definitions, the typed resource
//! layer for parsing declarative configuration (`resources` module), differ
//! layer for parsing declarative configuration (`resources` module), a
//! semantic-validation pass on top of that same model (`lint`), differ
//! event types shared with backend/CLI consumers, a generic JSON value-diff
//! utility, the `Backend` trait implemented by each gateway integration, and
//! the `Converter` trait implemented by each source-format converter.
//!
//! Not yet here: semantic validation (cross-field rules, regex, min/max) on
//! top of the `resources` types, and JSON Schema export. The differ's own
//! field-metadata table lives in `adc-differ` instead of here, since nothing
//! outside the differ consumes it.
//! `lint` is a separate call (`lint::lint`), not baked into `Deserialize` —
//! deserializing a `resources::Configuration` only ever enforces shape
//! (types, required fields, unknown fields); semantic rules run only when a
//! caller explicitly asks. The differ's own field-metadata table lives in
//! `adc-differ` instead of here, since nothing outside the differ consumes it.

pub mod backend;
pub mod converter;
pub mod default_value;
pub mod event;
pub mod lint;
pub mod resource;
pub mod resources;
pub mod utils;
Expand All @@ -26,7 +29,7 @@ pub use converter::{ConvertError, Converter};
pub use default_value::DefaultValue;
pub use event::{Event, EventKind, EventType};
pub use resource::{FieldListType, ResourceType};
pub use value_diff::{DiffPath, PathSegment, ValueDiff, diff_value};
pub use value_diff::{DiffPath, PathSegment, ValueDiff, diff_value, format_path};

use serde_json::{Map, Value};

Expand Down
Loading