Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update external DNS during blueprint execution #5212

Merged
merged 40 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
bb9d3df
WIP: initial changes to add external DNS generation
davepacheco Mar 7, 2024
c48aa07
WIP: initial changes to add external DNS to execution
davepacheco Mar 7, 2024
c7fad86
abandoned WIP: move nexus-external-endpoints into a crate
davepacheco Mar 7, 2024
34e5c3f
Revert "abandoned WIP: move nexus-external-endpoints into a crate"
davepacheco Mar 7, 2024
f2771fd
finish implementation
davepacheco Mar 7, 2024
7903de9
add basic test for external DNS computation
davepacheco Mar 7, 2024
206a7b0
WIP: Merge branch 'main' into dap/reconfigurator-external-dns
davepacheco Mar 7, 2024
453f851
move blueprint example() out of [cfg(test)]
davepacheco Mar 7, 2024
ca69595
local change to deploy one extra Nexus zone
davepacheco Mar 8, 2024
728537f
fixes
davepacheco Mar 8, 2024
139a28e
various fixes
davepacheco Mar 8, 2024
e88c2c4
that is not how you do an order-insensitive comparison
davepacheco Mar 8, 2024
ca1d630
WIP: test runner should more faithfully fake inventory so we can writ…
davepacheco Mar 8, 2024
ce06eee
simulate Clickhouse
davepacheco Mar 9, 2024
54dd75b
simulate Nexus
davepacheco Mar 9, 2024
b05dacd
WIP: add dendrite and Crucible Pantry, but the dendrite IP/ports are …
davepacheco Mar 9, 2024
0a153ea
that'll fix it
davepacheco Mar 9, 2024
757018e
fix more fallout in terrible ways
davepacheco Mar 9, 2024
50394fe
fix more of my new test
davepacheco Mar 9, 2024
6fa022e
fix another test
davepacheco Mar 9, 2024
d339979
fix warnings
davepacheco Mar 9, 2024
c0af241
Merge branch 'main' into dap/reconfigurator-external-dns
davepacheco Mar 9, 2024
f00e7e0
mismerged Cargo.lock? seems to have caused tons of spurious chrono wa…
davepacheco Mar 9, 2024
a259938
test runner could look more realistic
davepacheco Mar 9, 2024
4e7e05c
Merge remote-tracking branch 'origin/dap/test-runner' into dap/reconf…
davepacheco Mar 9, 2024
1b76792
remove spurious change
davepacheco Mar 9, 2024
1c22f48
progress on the test
davepacheco Mar 10, 2024
2b5c89b
flesh out more of the test
davepacheco Mar 10, 2024
2781013
pull out overridables
davepacheco Mar 10, 2024
9233548
Merge branch 'main' into dap/reconfigurator-external-dns
davepacheco Mar 10, 2024
d322a96
rustfmt
davepacheco Mar 10, 2024
b2cb116
get more tests working
davepacheco Mar 11, 2024
2e8d018
cleanup
davepacheco Mar 11, 2024
5648f86
do not panic
davepacheco Mar 11, 2024
c2041d8
commonize silo_dns_name()
davepacheco Mar 12, 2024
35946f3
Merge branch 'main' into dap/reconfigurator-external-dns
davepacheco Mar 12, 2024
7028e0b
review feedback
davepacheco Mar 13, 2024
7ccbc8d
Merge branch 'main' into dap/reconfigurator-external-dns
davepacheco Mar 13, 2024
a706479
implement schema update
davepacheco Mar 13, 2024
ef535d4
fix test
davepacheco Mar 13, 2024
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions clients/dns-service-client/src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,13 @@ impl<'a> DnsDiff<'a> {
&self,
) -> impl Iterator<Item = (&str, &[DnsRecord], &[DnsRecord])> {
self.left.iter().filter_map(|(k, v1)| match self.right.get(k) {
Some(v2) if v1 != v2 => {
Some((k.as_ref(), v1.as_ref(), v2.as_ref()))
Some(v2) => {
let mut v1_sorted = v1.clone();
let mut v2_sorted = v2.clone();
v1_sorted.sort();
v2_sorted.sort();
(v1_sorted != v2_sorted)
.then(|| (k.as_ref(), v1.as_ref(), v2.as_ref()))
}
_ => None,
})
Expand Down
39 changes: 39 additions & 0 deletions clients/dns-service-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,42 @@ impl types::DnsConfigParams {
Ok(&self.zones[0])
}
}

impl Ord for types::DnsRecord {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use types::DnsRecord;
match (self, other) {
// Same kinds: compare the items in them
(DnsRecord::A(addr1), DnsRecord::A(addr2)) => addr1.cmp(addr2),
(DnsRecord::Aaaa(addr1), DnsRecord::Aaaa(addr2)) => {
addr1.cmp(addr2)
}
(DnsRecord::Srv(srv1), DnsRecord::Srv(srv2)) => srv1
.target
.cmp(&srv2.target)
.then_with(|| srv1.port.cmp(&srv2.port)),

// Different kinds: define an arbitrary order among the kinds.
// We could use std::mem::discriminant() here but it'd be nice if
// this were stable over time.
// We define (arbitrarily): A < Aaaa < Srv
(DnsRecord::A(_), DnsRecord::Aaaa(_) | DnsRecord::Srv(_)) => {
std::cmp::Ordering::Less
}
(DnsRecord::Aaaa(_), DnsRecord::Srv(_)) => std::cmp::Ordering::Less,

// Anything else will result in "Greater". But let's be explicit.
(DnsRecord::Aaaa(_), DnsRecord::A(_))
| (DnsRecord::Srv(_), DnsRecord::A(_))
| (DnsRecord::Srv(_), DnsRecord::Aaaa(_)) => {
std::cmp::Ordering::Greater
}
}
}
}

impl PartialOrd for types::DnsRecord {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
5 changes: 4 additions & 1 deletion dev-tools/omdb/src/bin/omdb/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,10 @@ async fn cmd_nexus_blueprints_generate_from_collection(
)
.await
.context("creating blueprint from collection id")?;
eprintln!("created blueprint {} from collection id", blueprint.id);
eprintln!(
"created blueprint {} from collection id {}",
blueprint.id, args.collection_id
);
Ok(())
}

Expand Down
2 changes: 2 additions & 0 deletions dev-tools/reconfigurator-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ fn cmd_blueprint_from_inventory(
let blueprint = BlueprintBuilder::build_initial_from_collection(
collection,
dns_version,
dns_version,
&policy,
creator,
)
Expand Down Expand Up @@ -487,6 +488,7 @@ fn cmd_blueprint_plan(
sim.log.clone(),
parent_blueprint,
dns_version,
dns_version,
&policy,
creator,
collection,
Expand Down
4 changes: 3 additions & 1 deletion dev-tools/reconfigurator-cli/tests/input/complex.json
Original file line number Diff line number Diff line change
Expand Up @@ -7973,6 +7973,7 @@
],
"parent_blueprint_id": null,
"internal_dns_version": 1,
"external_dns_version": 1,
"time_created": "2024-03-12T19:09:41.953854Z",
"creator": "0d459323-e414-4f32-9944-76c7331da622",
"comment": "from collection 3f61b723-cfe7-40ec-b22c-e3e1f35325f9"
Expand Down Expand Up @@ -8645,9 +8646,10 @@
],
"parent_blueprint_id": null,
"internal_dns_version": 1,
"external_dns_version": 1,
"time_created": "2024-03-12T19:11:49.876621Z",
"creator": "0d459323-e414-4f32-9944-76c7331da622",
"comment": "from collection 5e1d6ae0-c5b4-4884-ac41-680cd4f5762d"
}
]
}
}
3 changes: 3 additions & 0 deletions nexus/db-model/src/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct Blueprint {
pub id: Uuid,
pub parent_blueprint_id: Option<Uuid>,
pub internal_dns_version: Generation,
pub external_dns_version: Generation,
pub time_created: DateTime<Utc>,
pub creator: String,
pub comment: String,
Expand All @@ -36,6 +37,7 @@ impl From<&'_ nexus_types::deployment::Blueprint> for Blueprint {
id: bp.id,
parent_blueprint_id: bp.parent_blueprint_id,
internal_dns_version: Generation(bp.internal_dns_version),
external_dns_version: Generation(bp.external_dns_version),
time_created: bp.time_created,
creator: bp.creator.clone(),
comment: bp.comment.clone(),
Expand All @@ -49,6 +51,7 @@ impl From<Blueprint> for nexus_types::deployment::BlueprintMetadata {
id: value.id,
parent_blueprint_id: value.parent_blueprint_id,
internal_dns_version: *value.internal_dns_version,
external_dns_version: *value.external_dns_version,
time_created: value.time_created,
creator: value.creator,
comment: value.comment,
Expand Down
3 changes: 2 additions & 1 deletion nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use omicron_common::api::external::SemverVersion;
///
/// This should be updated whenever the schema is changed. For more details,
/// refer to: schema/crdb/README.adoc
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(41, 0, 0);
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(42, 0, 0);

table! {
disk (id) {
Expand Down Expand Up @@ -1441,6 +1441,7 @@ table! {
comment -> Text,

internal_dns_version -> Int8,
external_dns_version -> Int8,
}
}

Expand Down
20 changes: 17 additions & 3 deletions nexus/db-queries/src/db/datastore/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ impl DataStore {
let (
parent_blueprint_id,
internal_dns_version,
external_dns_version,
time_created,
creator,
comment,
Expand All @@ -251,6 +252,7 @@ impl DataStore {
(
blueprint.parent_blueprint_id,
*blueprint.internal_dns_version,
*blueprint.external_dns_version,
blueprint.time_created,
blueprint.creator,
blueprint.comment,
Expand Down Expand Up @@ -480,6 +482,7 @@ impl DataStore {
zones_in_service,
parent_blueprint_id,
internal_dns_version,
external_dns_version,
time_created,
creator,
comment,
Expand Down Expand Up @@ -1271,6 +1274,7 @@ mod tests {
let blueprint = BlueprintBuilder::build_initial_from_collection(
&collection,
Generation::new(),
Generation::new(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely not a blocker on this PR: This makes me wonder if we should have typed generations like we're getting typed UUIDs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does seem good.

&policy,
"test",
)
Expand Down Expand Up @@ -1305,6 +1309,7 @@ mod tests {
let blueprint1 = BlueprintBuilder::build_initial_from_collection(
&collection,
Generation::new(),
Generation::new(),
&EMPTY_POLICY,
"test",
)
Expand Down Expand Up @@ -1432,11 +1437,13 @@ mod tests {

// Create a builder for a child blueprint. While we're at it, use a
// different DNS version to test that that works.
let new_dns_version = blueprint1.internal_dns_version.next();
let new_internal_dns_version = blueprint1.internal_dns_version.next();
let new_external_dns_version = new_internal_dns_version.next();
let mut builder = BlueprintBuilder::new_based_on(
&logctx.log,
&blueprint1,
new_dns_version,
new_internal_dns_version,
new_external_dns_version,
&policy,
"test",
)
Expand Down Expand Up @@ -1488,7 +1495,8 @@ mod tests {
.expect("failed to read collection back");
println!("diff: {}", blueprint2.diff_sleds(&blueprint_read).display());
assert_eq!(blueprint2, blueprint_read);
assert_eq!(blueprint2.internal_dns_version, new_dns_version);
assert_eq!(blueprint2.internal_dns_version, new_internal_dns_version);
assert_eq!(blueprint2.external_dns_version, new_external_dns_version);
{
let mut expected_ids = [blueprint1.id, blueprint2.id];
expected_ids.sort();
Expand Down Expand Up @@ -1581,6 +1589,7 @@ mod tests {
let blueprint1 = BlueprintBuilder::build_initial_from_collection(
&collection,
Generation::new(),
Generation::new(),
&EMPTY_POLICY,
"test1",
)
Expand All @@ -1589,6 +1598,7 @@ mod tests {
&logctx.log,
&blueprint1,
Generation::new(),
Generation::new(),
&EMPTY_POLICY,
"test2",
)
Expand All @@ -1598,6 +1608,7 @@ mod tests {
&logctx.log,
&blueprint1,
Generation::new(),
Generation::new(),
&EMPTY_POLICY,
"test3",
)
Expand Down Expand Up @@ -1695,6 +1706,7 @@ mod tests {
&logctx.log,
&blueprint3,
Generation::new(),
Generation::new(),
&EMPTY_POLICY,
"test3",
)
Expand Down Expand Up @@ -1734,6 +1746,7 @@ mod tests {
let blueprint1 = BlueprintBuilder::build_initial_from_collection(
&collection,
Generation::new(),
Generation::new(),
&EMPTY_POLICY,
"test1",
)
Expand All @@ -1742,6 +1755,7 @@ mod tests {
&logctx.log,
&blueprint1,
Generation::new(),
Generation::new(),
&EMPTY_POLICY,
"test2",
)
Expand Down
93 changes: 42 additions & 51 deletions nexus/db-queries/src/db/datastore/rack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use crate::db::model::Rack;
use crate::db::model::Zpool;
use crate::db::pagination::paginated;
use crate::db::pool::DbConnection;
use crate::transaction_retry::OptionalError;
use async_bb8_diesel::AsyncConnection;
use async_bb8_diesel::AsyncRunQueryDsl;
use chrono::Utc;
Expand All @@ -44,6 +43,8 @@ use nexus_db_model::PasswordHashString;
use nexus_db_model::SiloUser;
use nexus_db_model::SiloUserPasswordHash;
use nexus_db_model::SledUnderlaySubnetAllocation;
use nexus_types::deployment::Blueprint;
use nexus_types::deployment::OmicronZoneType;
use nexus_types::external_api::params as external_params;
use nexus_types::external_api::shared;
use nexus_types::external_api::shared::IdentityType;
Expand All @@ -54,6 +55,7 @@ use nexus_types::internal_api::params as internal_params;
use omicron_common::api::external::DataPageParams;
use omicron_common::api::external::Error;
use omicron_common::api::external::IdentityMetadataCreateParams;
use omicron_common::api::external::InternalContext;
use omicron_common::api::external::ListResultVec;
use omicron_common::api::external::LookupType;
use omicron_common::api::external::ResourceType;
Expand Down Expand Up @@ -793,60 +795,49 @@ impl DataStore {
pub async fn nexus_external_addresses(
&self,
opctx: &OpContext,
blueprint: Option<&Blueprint>,
) -> Result<(Vec<IpAddr>, Vec<DnsZone>), Error> {
opctx.authorize(authz::Action::Read, &authz::DNS_CONFIG).await?;

use crate::db::schema::external_ip::dsl as extip_dsl;
use crate::db::schema::service::dsl as service_dsl;

let err = OptionalError::new();
let conn = self.pool_connection_authorized(opctx).await?;
self.transaction_retry_wrapper("nexus_external_addresses")
.transaction(&conn, |conn| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the transaction was here to (attempt to?) ensure we got a consistent view of DNS zones + external IPs; does that not matter in practice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it should matter? The list of DNS zones cannot actually change today at all. If it did, I think you'd already have a problem because we're constructing a list of DNS names based on those zones, and that could become out of date by the time we try to write the DNS changes. The set of external IPs could, too, though only via Reconfigurator. Eventually I think we want to have even the Silo DNS changes happen through Reconfigurator, and then we'll know it will at least converge to correct. Anyway, since the list of DNS zones cannot change today, I think it makes sense to do this and revisit if/when we support that.

let err = err.clone();
async move {
let ips = extip_dsl::external_ip
.inner_join(
service_dsl::service.on(service_dsl::id
.eq(extip_dsl::parent_id.assume_not_null())),
)
.filter(extip_dsl::parent_id.is_not_null())
.filter(extip_dsl::time_deleted.is_null())
.filter(extip_dsl::is_service)
.filter(
service_dsl::kind.eq(db::model::ServiceKind::Nexus),
)
.select(ExternalIp::as_select())
.get_results_async(&conn)
.await?
.into_iter()
.map(|external_ip| external_ip.ip.ip())
.collect();

let dns_zones = self
.dns_zones_list_all_on_connection(
opctx,
&conn,
DnsGroup::External,
)
.await
.map_err(|e| match e.retryable() {
NotRetryable(not_retryable_err) => {
err.bail(not_retryable_err)
}
Retryable(retryable_err) => retryable_err,
})?;

Ok((ips, dns_zones))
}
})
let dns_zones = self
.dns_zones_list_all(opctx, DnsGroup::External)
.await
.map_err(|e| {
if let Some(err) = err.take() {
return err.into();
}
public_error_from_diesel(e, ErrorHandler::Server)
})
.internal_context("listing DNS zones to list external addresses")?;

let nexus_external_ips = if let Some(blueprint) = blueprint {
blueprint
.all_omicron_zones()
.filter_map(|(_, z)| match z.zone_type {
OmicronZoneType::Nexus { external_ip, .. } => {
Some(external_ip)
}
_ => None,
})
.collect()
} else {
use crate::db::schema::external_ip::dsl as extip_dsl;
use crate::db::schema::service::dsl as service_dsl;

let conn = self.pool_connection_authorized(opctx).await?;

extip_dsl::external_ip
.inner_join(service_dsl::service.on(
service_dsl::id.eq(extip_dsl::parent_id.assume_not_null()),
))
.filter(extip_dsl::parent_id.is_not_null())
.filter(extip_dsl::time_deleted.is_null())
.filter(extip_dsl::is_service)
.filter(service_dsl::kind.eq(db::model::ServiceKind::Nexus))
.select(ExternalIp::as_select())
.get_results_async(&*conn)
.await
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))?
.into_iter()
.map(|external_ip| external_ip.ip.ip())
.collect()
};

Ok((nexus_external_ips, dns_zones))
}
}

Expand Down
Loading
Loading